2017-04-05 49 views
-1

我在学习的SQL书中发现了一个练习,这个练习没有解决,我无法解决它。如何在Oracle 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 
); 

有什么建议?

+2

你是怎么试图解决它的?你有什么问题? –

+0

CREATE OR REPLACE TRIGGER CONTRACTOVERLAPPED INSERT后合同 FOR EACH ROW BEGIN *这部分是给我的问题,我不知道怎么表达,“如果在新合同已经存在CLIENT_ID,检查他是否有合同修改enddate“我知道如何更改日期,但我不知道如何检查条件。 END; @Alex Poole –

+1

@KekaBron,请将您尝试编码的代码添加到您的问题中,并告诉我们究竟哪个部分导致了哪个错误。此外,当用户输入错误数据或者是否想根据某种逻辑设置字段(如将其设置为最新合约的结束日期前一天)时,是否要抛出错误? – Rachcha

回答

0

我同意发布的评论,它有助于对先前尝试中失败的内容进行一些具体说明,并且我还建议不要使用TRIGGER来完成此类事情。
但是,因为这是一个学习练习,这里有一些例子可能是一个起点。

我在这些例子中修改了你的表以禁止NULLPRIMARY KEY s。

要开始,创建表:

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,'[email protected]',NULL,NULL); 
INSERT INTO CLIENTS VALUES (2,NULL,'Chewbacca','UNKNOWN',NULL,'[email protected]',NULL,NULL); 
COMMIT; 

然后创建一个TRIGGER。在第一个示例中,TRIGGERAFTER STATEMENT类型。
它很简单但效率低,因为它在每个INSERT声明后每CLIENT评估
对于大数据集,或面对多个TRIGGER s,这可能是一个问题。
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; 
/

然后,测试一下。
添加初始合同。预计没有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    

然后添加新的合同。
佛罗多的新合同在其现有合同结束前开始,因此结尾日期将进行调整。
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    

要稳定在此查询的工作量,复合触发器可以替代使用。该第二示例实现相同的结果作为第一,但只询问CONTRACT S的CLIENT S作改变:

首先,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    
+0

wao我只是觉得这个练习会有一个简单/简短的回答,因为这个练习的陈述很简短。但我可以看到它没有。自从我几周前开始阅读我的书以来,我还没有足够的水平来理解您发布的所有内容,并且只是阅读了一些关于触发器的规则和基本语法。反正谢谢你。我要学习很多东西,并会让我的老师疯狂地问他所有这些句子哈哈。 –

+0

谢谢@KekaBron对于'TRIGGER's,通常会考虑副作用和性能方面的考虑因素。通常,如果其他选项(如使用过程)可以满足需要,则可以完全避免使用触发器。在你的学习中最好的祝愿。如果您对此帖有任何疑问,请告诉我。谢谢 – alexgibbs