如何在Oracle 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

我在我学习的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
);

有什么建议吗?

我同意贴出的评论,这有助于了解先前尝试中失败的细节,我还建议不要对此类事情使用
触发器。
但由于这是一个学习练习,这里有一些例子可能是一个起点

在这些示例中,我已经修改了您的表,不允许使用
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 with
TRIGGER
s通常会考虑到副作用和性能问题。如果其他选项(如使用过程)可以满足需要,则通常可以完全避免触发器。祝你学习愉快。如果你对这篇文章有任何疑问,请告诉我。谢谢