Wat je bent tegengekomen is de klassieke uitzondering "mutating table". In een ROW-trigger staat Oracle niet toe dat u een query uitvoert op de tabel waarop de trigger is gedefinieerd - dus het is de SELECT
tegen TABEL1 in de DELETING
een deel van de trigger die dit probleem veroorzaakt.
Er zijn een aantal manieren om dit te omzeilen. Misschien is het in deze situatie het beste om een samengestelde trigger te gebruiken, die er ongeveer zo uitziet:
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;
Een samengestelde trigger staat elk timingpunt toe (BEFORE STATEMENT
, BEFORE ROW
, AFTER ROW
, en AFTER STATEMENT
) te behandelen. Merk op dat de timingpunten altijd in de aangegeven volgorde worden aangeroepen. Wanneer een geschikte SQL-instructie (d.w.z. INSERT INTO TABLE1
of DELETE FROM TABLE1
) wordt uitgevoerd en deze trigger wordt geactiveerd. Het eerste timingpunt dat wordt aangeroepen is BEFORE STATEMENT
, en de code in de BEFORE STATEMENT
handler zal een PL/SQL-tabel toewijzen om een aantal getallen te bevatten. In dit geval zijn de nummers die in de PL/SQL-tabel moeten worden opgeslagen, de TABLE2_ID-waarden uit TABLE1. (Er wordt een PL/SQL-tabel gebruikt in plaats van bijvoorbeeld een array omdat een tabel een wisselend aantal waarden kan bevatten, terwijl als we een array zouden gebruiken, we van tevoren zouden moeten weten hoeveel getallen we zouden moeten opslaan. We kunnen niet van tevoren weten hoeveel rijen worden beïnvloed door een bepaalde instructie, dus gebruiken we een PL/SQL-tabel).
Wanneer de AFTER EACH ROW
timingpunt is bereikt en we ontdekken dat de instructie die wordt verwerkt een INSERT is, de trigger gaat gewoon door en voert de noodzakelijke UPDATE naar TABLE2 uit, omdat dit geen probleem zal veroorzaken. Als er echter een DELETE wordt uitgevoerd, slaat de trigger de TABLE1.TABLE2_ID op in de eerder toegewezen PL/SQL-tabel. Wanneer de AFTER STATEMENT
timingpunt eindelijk is bereikt, wordt de eerder toegewezen PL/SQL-tabel doorlopen en wordt voor elke gevonden TABLE2_ID de juiste update uitgevoerd.
Documentatie hier.