如何在Oracle SQL上实现此触发器?

时间:2017-04-05 18:01:57

标签: oracle oracle11g triggers

我在我研究的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
);

有什么建议吗?

1 个答案:

答案 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。在第一个示例中,TRIGGERAFTER 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。第二个示例实现了与第一个示例相同的结果,但仅询问已更改的CONTRACTCLIENT

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