2017-04-05 21 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