sql >> Database >  >> RDS >> Oracle

Hoe deze trigger te implementeren op Oracle SQL?

Ik ben het eens met de geposte opmerkingen dat het helpt om wat details te hebben over wat er mis is in de eerdere pogingen, en ik zou ook aanraden om geen TRIGGER te gebruiken helemaal niet voor dit soort dingen.
Maar aangezien dit voor een studie-oefening is, zijn hier enkele voorbeelden die een startpunt kunnen zijn.

Ik heb uw tabellen aangepast om NULL niet toe te staan PRIMARY KEY s in deze voorbeelden.

Maak om te beginnen de tabellen:

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
);

Maak vervolgens de eerste 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;

Maak vervolgens een TRIGGER . In dit eerste voorbeeld is de TRIGGER is een AFTER STATEMENT type.
Het is eenvoudig maar inefficiënt omdat het elke . evalueert CLIENT na elke INSERT statement.
Tegen een grote dataset, of in het licht van meerdere TRIGGER s, kan dit een probleem zijn.
Deze TRIGGER zal controleren op het vorige contract en zal zijn ENDDATE . instellen tot één dag voor het nieuwe contract, als het nietig is of na de start van het nieuwe contract.

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;
  /

Test het dan uit.
Voeg de eerste contracten toe. Er worden geen einddatumwijzigingen verwacht, aangezien dit de eerste zijn. Frodo's contract hier heeft al een einddatum.

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             

Voeg vervolgens nieuwe contracten toe.
Frodo's nieuwe contract begint voor het einde van zijn bestaande contract, dus de einddatum zal worden aangepast.
Chewie's oorspronkelijke contract had geen EINDDATUM, dus het zal ook worden aangepast.

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             

En terwijl andere contracten worden ondertekend, gaat het patroon verder:

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             

Om de werklast op deze query te stabiliseren, kan in plaats daarvan een COMPOUND TRIGGER worden gebruikt. Dit tweede voorbeeld bereikt hetzelfde resultaat als het eerste, maar ondervraagt ​​alleen het CONTRACT s van CLIENT s die zijn gewijzigd:

Eerst ROLLBACK; Maak vervolgens een type dat moet worden gebruikt door de TRIGGER :

CREATE OR REPLACE TYPE NUMBER_LIST IS TABLE OF NUMBER;
/

Maak vervolgens de 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;
/

Na het herhalen van de bovenstaande invoegingen is het resultaat hetzelfde:

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             



  1. Wijzig database-sortering, Ctype in Postgresql

  2. Hoe zoekresultaten op afstand sorteren in Laravel QueryBuilder / MySQL Spatial-pakket?

  3. PostgreSQL+table-partitionering:inefficiënte max() en min()

  4. MySQL COUNT retourneert False of True niet in PHP