Det, du er stødt på, er den klassiske "mutationstabel"-undtagelse. I en ROW trigger tillader Oracle dig ikke at køre en forespørgsel mod den tabel, som triggeren er defineret på - så det er SELECT
mod TABEL1 i DELETING
del af udløseren, der forårsager dette problem.
Der er et par måder at omgå dette på. Måske er det bedste i denne situation at bruge en sammensat trigger, som ville se nogenlunde sådan ud:
CREATE OR REPLACE TRIGGER TABLE1_NUM_TRG
FOR INSERT OR DELETE ON TABLE1
COMPOUND TRIGGER
TYPE NUMBER_TABLE IS TABLE OF NUMBER;
tblTABLE2_IDS NUMBER_TABLE;
BEFORE STATEMENT IS
BEGIN
tblTABLE2_IDS := NUMBER_TABLE();
END BEFORE STATEMENT;
AFTER EACH ROW IS
BEGIN
IF INSERTING THEN
UPDATE TABLE2 t2
SET t2.TABLE2NUM = :new.NUM
WHERE t2.ID = :new.TABLE2_ID;
ELSIF DELETING THEN
tblTABLE2_IDS.EXTEND;
tblTABLE2_IDS(tblTABLE2_IDS.LAST) := :new.TABLE2_ID;
END IF;
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
IF tblTABLE2_IDS.COUNT > 0 THEN
FOR i IN tblTABLE2_IDS.FIRST..tblTABLE2_IDS.LAST LOOP
UPDATE TABLE2 t2
SET t2.TABLE2NUM = (SELECT NUM
FROM (SELECT t1.NUM
FROM TABLE1 t1
WHERE t1.TABLE2_ID = tblTABLE2_IDS(i)
ORDER BY modification_date DESC)
WHERE ROWNUM = 1)
WHERE t2.ID = tblTABLE2_IDS(i);
END LOOP;
END IF;
END AFTER STATEMENT;
END TABLE1_NUM_TRG;
En sammensat trigger tillader hvert tidspunkt (BEFORE STATEMENT
, BEFORE ROW
, AFTER ROW
, og AFTER STATEMENT
) skal håndteres. Bemærk, at tidspunkterne altid påberåbes i den angivne rækkefølge. Når en passende SQL-sætning (dvs. INSERT INTO TABLE1
eller DELETE FROM TABLE1
) udføres, og denne udløser udløses, det første tidspunkt, der skal påkaldes, vil være BEFORE STATEMENT
, og koden i BEFORE STATEMENT
handler vil tildele en PL/SQL-tabel til at indeholde en masse tal. I dette tilfælde vil tallene, der skal gemmes i PL/SQL-tabellen, være TABLE2_ID-værdierne fra TABLE1. (En PL/SQL-tabel bruges i stedet for for eksempel en matrix, fordi en tabel kan indeholde et varierende antal værdier, mens hvis vi brugte en matrix, skulle vi på forhånd vide, hvor mange tal vi skal gemme. Vi kan ikke på forhånd vide, hvor mange rækker der vil blive påvirket af en bestemt sætning, så vi bruger en PL/SQL-tabel).
Når AFTER EACH ROW
tidspunktet er nået, og vi finder ud af, at sætningen, der behandles, er en INSERT, triggeren går bare videre og udfører den nødvendige OPDATERING til TABLE2, da dette ikke vil forårsage et problem. Men hvis en DELETE udføres, gemmer triggeren TABLE1.TABLE2_ID i den tidligere tildelte PL/SQL-tabell. Når AFTER STATEMENT
tidspunktet er endelig nået, den tidligere allokerede PL/SQL-tabell gentages, og for hver fundet TABLE2_ID udføres den passende opdatering.
Dokumentation her.