To, na co natknąłeś się, to klasyczny wyjątek „tabeli mutacji”. W wyzwalaczu ROW Oracle nie pozwala na uruchomienie zapytania względem tabeli, w której zdefiniowano wyzwalacz — więc jest to SELECT
względem TABLE1 w DELETING
część wyzwalacza, który powoduje ten problem.
Jest kilka sposobów na obejście tego. Być może najlepszym rozwiązaniem w tej sytuacji jest użycie wyzwalacza złożonego, który wygląda następująco:
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;
Wyzwalacz złożony zezwala na każdy punkt czasowy (BEFORE STATEMENT
, BEFORE ROW
, AFTER ROW
i AFTER STATEMENT
) do rozpatrzenia. Zauważ, że punkty czasowe są zawsze wywoływane w podanej kolejności. Kiedy odpowiednia instrukcja SQL (np. INSERT INTO TABLE1
lub DELETE FROM TABLE1
) jest wykonywany i wyzwalacz jest uruchamiany, a pierwszym punktem czasowym do wywołania będzie BEFORE STATEMENT
i kod w BEFORE STATEMENT
handler przydzieli tabelę PL/SQL do przechowywania wielu liczb. W tym przypadku liczbami, które mają być przechowywane w tabeli PL/SQL będą wartości TABLE2_ID z TABLE1. (Tabela PL/SQL jest używana zamiast na przykład tablicy, ponieważ tabela może przechowywać różną liczbę wartości, podczas gdy gdybyśmy używali tablicy, musielibyśmy z góry wiedzieć, ile liczb będziemy musieli przechowywać. Nie możemy z góry wiedzieć, na ile wierszy wpłynie dana instrukcja, więc używamy tabeli PL/SQL).
Gdy AFTER EACH ROW
Osiągnięto punkt czasowy i stwierdziliśmy, że przetwarzana instrukcja to INSERT, wyzwalacz po prostu kontynuuje działanie i wykonuje niezbędną aktualizację do tabeli TABLE2, ponieważ nie spowoduje to problemu. Jeśli jednak wykonywane jest polecenie DELETE, wyzwalacz zapisuje TABLE1.TABLE2_ID w przydzielonej wcześniej tabeli PL/SQL. Gdy AFTER STATEMENT
w końcu osiągnięty został punkt czasowy, przydzielona wcześniej tabela PL/SQL jest iterowana i dla każdego znalezionego identyfikatora TABLE2_ID przeprowadzana jest odpowiednia aktualizacja.
Dokumentacja tutaj.