Det du har stött på är det klassiska undantaget "muterande tabell". I en ROW-utlösare tillåter inte Oracle dig att köra en fråga mot tabellen som utlösaren är definierad på - så det är SELECT
mot TABELL1 i DELETING
del av utlösaren som orsakar det här problemet.
Det finns ett par sätt att kringgå detta. Det bästa i den här situationen är kanske att använda en sammansatt trigger, som skulle se ut ungefär så här:
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 sammansatt trigger tillåter varje tidpunkt (BEFORE STATEMENT
, BEFORE ROW
, AFTER ROW
och AFTER STATEMENT
) som ska hanteras. Observera att tidpunkterna alltid åberopas i den ordning som anges. När en lämplig SQL-sats (dvs. INSERT INTO TABLE1
eller DELETE FROM TABLE1
) exekveras och den här utlösaren aktiveras. Den första tidpunkten som ska anropas kommer att vara BEFORE STATEMENT
, och koden i BEFORE STATEMENT
hanteraren kommer att allokera en PL/SQL-tabell för att hålla ett gäng nummer. I detta fall kommer numren som ska lagras i PL/SQL-tabellen att vara TABLE2_ID-värdena från TABLE1. (En PL/SQL-tabell används istället för till exempel en array eftersom en tabell kan innehålla ett varierande antal värden, medan om vi använde en array skulle vi behöva veta i förväg hur många nummer vi skulle behöva lagra. Vi kan inte i förväg veta hur många rader som kommer att påverkas av en viss sats, så vi använder en PL/SQL-tabell).
När AFTER EACH ROW
tidpunkten nås och vi finner att satsen som bearbetas är en INSERT, utlösaren går bara vidare och utför den nödvändiga UPPDATERING till TABELL2 eftersom detta inte kommer att orsaka några problem. Men om en DELETE utförs sparar triggern TABLE1.TABLE2_ID i PL/SQL-tabellen som allokerats tidigare. När AFTER STATEMENT
tidpunkten nås äntligen, PL/SQL-tabellen som tilldelats tidigare itereras igenom, och för varje hittat TABLE2_ID utförs lämplig uppdatering.
Dokumentation här.