我在我研究的SQL书中找到了一个练习,这个练习没有解决,我无法解决。
目标是实现避免重叠合同的触发器。如果具有当前合同的客户签署新合同,则前一个合同的结束日期将是新开始日期之前的一天。
给出的表格是:
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
);
有什么建议吗?
答案 0 :(得分:0)
我同意所发布的评论,它有助于了解先前尝试中失败的内容,我还建议不要使用TRIGGER
来做这类事情。
但是,这是一个研究练习,这里有一些可能是起点的例子。
我在这些示例中修改了您的表格以禁止NULL
PRIMARY KEY
。
首先,创建表格:
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
);
然后,创建第一个CLIENT
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;
然后创建一个TRIGGER
。在第一个示例中,TRIGGER
是AFTER STATEMENT
类型
这很简单但效率很低,因为它会在每个CLIENT
语句后评估每个 INSERT
。
对于大型数据集,或面对多个TRIGGER
,这可能是一个问题
此TRIGGER
将检查先前的合同,并将其ENDDATE
设置为新合同前一天(如果它为空或新合同开始后)。
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;
/
然后,测试一下。
添加初始合同。没有预期的最终更改,因为这些是第一个。佛罗多的合同已经确定了结束日期。
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
然后添加新合同 佛罗多的新合同在现有合同结束前开始,因此将终止合同 Chewie的初始合同没有ENDDATE,因此也将进行调整。
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
要稳定此查询的工作负载,可以使用COMPOUND TRIGGER。第二个示例实现了与第一个示例相同的结果,但仅询问已更改的CONTRACT
个CLIENT
:
首先,ROLLBACK;
然后,创建一个由TRIGGER
使用的类型:
CREATE OR REPLACE TYPE NUMBER_LIST IS TABLE OF NUMBER;
/
然后创建COMPOUND TRIGGER
:
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