Това, на което сте се сблъскали, е класическото изключение за "мутираща таблица". В ROW тригер Oracle не ви позволява да стартирате заявка към таблицата, върху която е дефиниран тригерът - така че това е SELECT
срещу TABLE1 в DELETING
част от спусъка, който причинява този проблем.
Има няколко начина да заобиколите това. Може би най-доброто в тази ситуация е да използвате комбиниран тригер, който би изглеждал нещо като:
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;
Съставният тригер позволява всяка времева точка (BEFORE STATEMENT
, BEFORE ROW
, AFTER ROW
и AFTER STATEMENT
) да се обработва. Обърнете внимание, че точките за синхронизация винаги се извикват в дадения ред. Когато подходящ SQL израз (т.е. INSERT INTO TABLE1
или DELETE FROM TABLE1
) се изпълнява и този тригер се задейства, първата времева точка, която трябва да бъде извикана, ще бъде BEFORE STATEMENT
и кода в BEFORE STATEMENT
манипулаторът ще разпредели PL/SQL таблица, за да съдържа куп числа. В този случай числата, които ще се съхраняват в таблицата PL/SQL, ще бъдат стойностите на TABLE2_ID от TABLE1. (Таблица PL/SQL се използва вместо, например, масив, защото таблицата може да съдържа различен брой стойности, докато ако използваме масив, ще трябва да знаем предварително колко числа ще трябва да съхраняваме. Не можем да знаем предварително колко реда ще бъдат засегнати от определен израз, затова използваме PL/SQL таблица).
Когато AFTER EACH ROW
моментът е достигнат и откриваме, че операторът, който се обработва, е INSERT, тригерът просто продължава и извършва необходимото АКТУАЛИЗИРАНЕ на TABLE2, тъй като това няма да причини проблем. Въпреки това, ако се изпълнява DELETE, тригерът записва TABLE1.TABLE2_ID в PL/SQL таблицата, разпределена по-рано. Когато AFTER STATEMENT
моментът на времето най-накрая е достигнат, PL/SQL таблицата, разпределена по-рано, се повтаря и за всеки намерен TABLE2_ID се извършва съответната актуализация.
Документация тук.