如何在Oracle SQL上实现此触发器?
我在我学习的SQL书籍中发现了一个练习,它没有解决,我也无法解决 目标是实现一个触发器,以避免重复合同。如果具有当前合同的客户签署了新合同,则前一合同的结束日期将在新开始日期前一天 所提供的表格如下:如何在Oracle SQL上实现此触发器?,oracle,oracle11g,triggers,Oracle,Oracle11g,Triggers,我在我学习的SQL书籍中发现了一个练习,它没有解决,我也无法解决 目标是实现一个触发器,以避免重复合同。如果具有当前合同的客户签署了新合同,则前一合同的结束日期将在新开始日期前一天 所提供的表格如下: CREATE TABLE CLIENTS ( clientId VARCHAR2(15), DNI VARCHAR2(9), name VARCHAR2(100) NOT NULL, surname VARCHAR2(100) NOT NULL, sec_sur
CREATE TABLE CLIENTS (
clientId VARCHAR2(15),
DNI VARCHAR2(9),
name VARCHAR2(100) NOT NULL,
surname VARCHAR2(100) NOT NULL,
sec_surname VARCHAR2(100),
eMail VARCHAR2(100) NOT NULL,
phoneN NUMBER(12),
birthdate DATE,
CONSTRAINT PK_CLIENTS PRIMARY KEY (clientId),
CONSTRAINT UK1_CLIENTS UNIQUE (DNI),
CONSTRAINT UK2_CLIENTS UNIQUE (eMail),
CONSTRAINT UK3_CLIENTS UNIQUE (phoneN),
);
CREATE TABLE contracts(
contractId VARCHAR2(10),
clientId VARCHAR2(15),
startdate DATE NOT NULL,
enddate DATE,
contract_type VARCHAR2(50),
address VARCHAR2(100) NOT NULL,
town VARCHAR2(100) NOT NULL,
ZIPcode VARCHAR2(8) NOT NULL,
country VARCHAR2(100) NOT NULL,
CONSTRAINT PK_contracts PRIMARY KEY (contractId),
CONSTRAINT FK_contracts1 FOREIGN KEY (clientId) REFERENCES CLIENTS
);
有什么建议吗?我同意贴出的评论,这有助于了解先前尝试中失败的细节,我还建议不要对此类事情使用
触发器。
但由于这是一个学习练习,这里有一些例子可能是一个起点
在这些示例中,我已经修改了您的表,不允许使用NULL
主键
要开始,请创建表:
CREATE TABLE CLIENTS (
CLIENTID VARCHAR2(15) NOT NULL,
DNI VARCHAR2(9),
NAME VARCHAR2(100) NOT NULL,
SURNAME VARCHAR2(100) NOT NULL,
SEC_SURNAME VARCHAR2(100),
EMAIL VARCHAR2(100) NOT NULL,
PHONEN NUMBER(12),
BIRTHDATE DATE,
CONSTRAINT PK_CLIENTS PRIMARY KEY (CLIENTID),
CONSTRAINT UK1_CLIENTS UNIQUE (DNI),
CONSTRAINT UK2_CLIENTS UNIQUE (EMAIL),
CONSTRAINT UK3_CLIENTS UNIQUE (PHONEN)
);
CREATE TABLE CONTRACTS (
CONTRACTID VARCHAR2(10) NOT NULL,
CLIENTID VARCHAR2(15) NOT NULL,
STARTDATE DATE NOT NULL,
ENDDATE DATE,
CONTRACT_TYPE VARCHAR2(50),
ADDRESS VARCHAR2(100) NOT NULL,
TOWN VARCHAR2(100) NOT NULL,
ZIPCODE VARCHAR2(8) NOT NULL,
COUNTRY VARCHAR2(100) NOT NULL,
CONSTRAINT PK_CONTRACTS PRIMARY KEY (CONTRACTID),
CONSTRAINT FK_CONTRACTS1 FOREIGN KEY (CLIENTID) REFERENCES CLIENTS
);
然后,创建第一个客户端
s:
INSERT INTO CLIENTS VALUES (1,NULL,'Frodo','Baggins',NULL,'the.real.frodo@adventure.com',NULL,NULL);
INSERT INTO CLIENTS VALUES (2,NULL,'Chewbacca','UNKNOWN',NULL,'chewio.@kashyyyk.org',NULL,NULL);
COMMIT;
然后创建一个触发器
。在第一个示例中,触发器
是一个AFTER语句
类型。
它很简单,但效率低下,因为它在每个INSERT
语句之后对每个客户机进行求值。
对于大型数据集,或者面对多个触发器,这可能是一个问题。
此触发器
将检查先前的合同,并将其结束日期
设置为新合同前一天(如果为空或新合同开始后)
CREATE OR REPLACE TRIGGER CONTRACT_ENDDATE_ADJUSTER
AFTER INSERT ON CONTRACTS
BEGIN
MERGE INTO CONTRACTS
USING (
SELECT CONTRACTID,
CANDIDATE_ENDDATE AS ENDDATE
FROM
(SELECT CONTRACTS.CONTRACTID,
(TRUNC(LEAD(STARTDATE) OVER (PARTITION BY CLIENTID ORDER BY STARTDATE ASC) - 1)) AS CANDIDATE_ENDDATE,
DENSE_RANK() OVER (PARTITION BY CLIENTID ORDER BY STARTDATE DESC) AS CONTRACT_ORDER
FROM CONTRACTS)
WHERE CONTRACT_ORDER = 2) CANDIDATE_CONTRACT
ON (CONTRACTS.CONTRACTID = CANDIDATE_CONTRACT.CONTRACTID)
WHEN MATCHED THEN UPDATE SET CONTRACTS.ENDDATE = CANDIDATE_CONTRACT.ENDDATE
WHERE CONTRACTS.ENDDATE IS NULL OR CONTRACTS.ENDDATE > CANDIDATE_CONTRACT.ENDDATE;
END;
/
然后,测试它。
添加初始合同。预期不会有enddate更改,因为这是第一次更改。佛罗多在这里的合同已经确定了结束日期
INSERT INTO CONTRACTS VALUES('Break-Ring',1,TO_DATE('19560511','YYYYMMDD'), TO_DATE('19851014','YYYYMMDD'), NULL, 'No 1', 'Doom Mountain', 'MORD', 'Middle-Earth');
INSERT INTO CONTRACTS VALUES('SaveGalaxy',2,TO_DATE('19770615','YYYYMMDD'), NULL, NULL, 'No 75', 'Rwookrrorro', 'RWKR', 'Kashyyyk');
SELECT CONTRACTID, CLIENTID, STARTDATE, ENDDATE FROM CONTRACTS ORDER BY CLIENTID ASC, STARTDATE ASC;
CONTRACTID CLIENTID STARTDATE ENDDATE
Break-Ring 1 11-MAY-56 14-OCT-85
SaveGalaxy 2 15-JUN-77
然后添加新合同。
佛罗多的新合同在其现有合同结束之前开始,因此终止日期将进行调整。
丘伊的初始合同没有终止日期,因此也将进行调整
INSERT INTO CONTRACTS VALUES('GoBackHome',1,TO_DATE('19570219','YYYYMMDD'), NULL, NULL, 'No 13', 'Hobbiton', 'HBTN', 'Middle-Earth');
INSERT INTO CONTRACTS VALUES('DefendHoth',2,TO_DATE('19801115','YYYYMMDD'), NULL, NULL, 'Meteor Crater', 'Ice Ridge', 'METEO', 'Hoth');
SELECT CONTRACTID, CLIENTID, STARTDATE, ENDDATE FROM CONTRACTS ORDER BY CLIENTID ASC, STARTDATE ASC;
CONTRACTID CLIENTID STARTDATE ENDDATE
Break-Ring 1 11-MAY-56 18-FEB-57
GoBackHome 1 19-FEB-57
SaveGalaxy 2 15-JUN-77 14-NOV-80
DefendHoth 2 15-NOV-80
随着其他合同的签订,这种模式将继续下去:
INSERT INTO CONTRACTS VALUES('GoWedding',2,TO_DATE('19830309','YYYYMMDD'), NULL, NULL, 'Main Hall', 'Grand Palace', 'ALLNC', 'Coruscant');
INSERT INTO CONTRACTS VALUES('Gardening',1,TO_DATE('19570503','YYYYMMDD'), NULL, NULL, 'No 13', 'Hobbiton', 'HBTN', 'Middle-Earth');
SELECT CONTRACTID, CLIENTID, STARTDATE, ENDDATE FROM CONTRACTS ORDER BY CLIENTID ASC, STARTDATE ASC;
CONTRACTID CLIENTID STARTDATE ENDDATE
Break-Ring 1 11-MAY-56 18-FEB-57
GoBackHome 1 19-FEB-57 02-MAY-57
Gardening 1 03-MAY-57
SaveGalaxy 2 15-JUN-77 14-NOV-80
DefendHoth 2 15-NOV-80 08-MAR-83
GoWedding 2 09-MAR-83
为了稳定此查询上的工作负载,可以使用复合触发器。第二个示例获得与第一个示例相同的结果,但仅询问已更改的客户的合同
s:
首先,回滚代码>
然后,创建要由触发器使用的类型:
CREATE OR REPLACE TYPE NUMBER_LIST IS TABLE OF NUMBER;
/
CREATE OR REPLACE TRIGGER CONTRACT_ENDDATE_ADJUSTER
FOR INSERT ON CONTRACTS
COMPOUND TRIGGER
V_CLIENTS NUMBER_LIST;
BEFORE STATEMENT
IS
BEGIN
V_CLIENTS:= NUMBER_LIST();
END BEFORE STATEMENT;
AFTER EACH ROW
IS
BEGIN
V_CLIENTS.EXTEND();
V_CLIENTS(V_CLIENTS.COUNT) := :NEW.CLIENTID;
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
MERGE INTO CONTRACTS
USING (
SELECT CONTRACTID,
CANDIDATE_ENDDATE AS ENDDATE
FROM
(SELECT CONTRACTS.CONTRACTID,
(TRUNC(LEAD(STARTDATE) OVER (PARTITION BY CLIENTID ORDER BY STARTDATE ASC) - 1)) AS CANDIDATE_ENDDATE,
DENSE_RANK() OVER (PARTITION BY CLIENTID ORDER BY STARTDATE DESC) AS CONTRACT_ORDER
FROM CONTRACTS
WHERE CONTRACTS.CLIENTID IN (SELECT * FROM TABLE(V_CLIENTS)))
WHERE CONTRACT_ORDER = 2) CANDIDATE_CONTRACT
ON (CONTRACTS.CONTRACTID = CANDIDATE_CONTRACT.CONTRACTID)
WHEN MATCHED THEN UPDATE SET CONTRACTS.ENDDATE = CANDIDATE_CONTRACT.ENDDATE
WHERE CONTRACTS.ENDDATE IS NULL OR CONTRACTS.ENDDATE > CANDIDATE_CONTRACT.ENDDATE;
END AFTER STATEMENT;
END CONTRACT_ENDDATE_ADJUSTER;
/
然后创建复合触发器
:
CREATE OR REPLACE TYPE NUMBER_LIST IS TABLE OF NUMBER;
/
CREATE OR REPLACE TRIGGER CONTRACT_ENDDATE_ADJUSTER
FOR INSERT ON CONTRACTS
COMPOUND TRIGGER
V_CLIENTS NUMBER_LIST;
BEFORE STATEMENT
IS
BEGIN
V_CLIENTS:= NUMBER_LIST();
END BEFORE STATEMENT;
AFTER EACH ROW
IS
BEGIN
V_CLIENTS.EXTEND();
V_CLIENTS(V_CLIENTS.COUNT) := :NEW.CLIENTID;
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
MERGE INTO CONTRACTS
USING (
SELECT CONTRACTID,
CANDIDATE_ENDDATE AS ENDDATE
FROM
(SELECT CONTRACTS.CONTRACTID,
(TRUNC(LEAD(STARTDATE) OVER (PARTITION BY CLIENTID ORDER BY STARTDATE ASC) - 1)) AS CANDIDATE_ENDDATE,
DENSE_RANK() OVER (PARTITION BY CLIENTID ORDER BY STARTDATE DESC) AS CONTRACT_ORDER
FROM CONTRACTS
WHERE CONTRACTS.CLIENTID IN (SELECT * FROM TABLE(V_CLIENTS)))
WHERE CONTRACT_ORDER = 2) CANDIDATE_CONTRACT
ON (CONTRACTS.CONTRACTID = CANDIDATE_CONTRACT.CONTRACTID)
WHEN MATCHED THEN UPDATE SET CONTRACTS.ENDDATE = CANDIDATE_CONTRACT.ENDDATE
WHERE CONTRACTS.ENDDATE IS NULL OR CONTRACTS.ENDDATE > CANDIDATE_CONTRACT.ENDDATE;
END AFTER STATEMENT;
END CONTRACT_ENDDATE_ADJUSTER;
/
重复上述插入后,结果相同:
CONTRACTID CLIENTID STARTDATE ENDDATE
Break-Ring 1 11-MAY-56 18-FEB-57
GoBackHome 1 19-FEB-57 02-MAY-57
Gardening 1 03-MAY-57
SaveGalaxy 2 15-JUN-77 14-NOV-80
DefendHoth 2 15-NOV-80 08-MAR-83
GoWedding 2 09-MAR-83
您是如何尝试解决的,您遇到了什么问题?创建或替换触发器合同在插入每行合同后重叠开始*这部分给我带来了问题,我不知道如何表达“如果新合同中的客户id已经存在,请检查他是否有合同并修改结束日期”我知道如何更改日期,但我不知道如何检查条件。结束@亚历克斯Poole@KekaBron,请将您尝试编辑的代码添加到您的问题中,并准确地告诉我们是哪个部分导致了哪个错误。此外,您的意思是当用户输入错误数据时抛出错误,还是希望按照某种逻辑设置字段(例如将其设置为最新合同结束日期前一天)?wao我只是认为此练习的答案很简单/简短,因为练习的陈述很短。但我看不出来。自从几周前我开始阅读我的书,只是读了一些关于触发器的规则和基本语法之后,我还没有足够的水平来理解你们发布的所有内容。无论如何,谢谢你。我会学到很多东西,并且会让我的老师发疯,问他这些句子。哈哈。谢谢@KekaBron Yeah withTRIGGER
s通常会考虑到副作用和性能问题。如果其他选项(如使用过程)可以满足需要,则通常可以完全避免触发器。祝你学习愉快。如果你对这篇文章有任何疑问,请告诉我。谢谢