Quello in cui ti sei imbattuto è la classica eccezione "tabella mutante". In un trigger ROW Oracle non ti consente di eseguire una query sulla tabella su cui è definito il trigger, quindi è il SELECT
contro TABLE1 nel DELETING
parte del trigger che causa questo problema.
Ci sono un paio di modi per aggirare questo problema. Forse la cosa migliore in questa situazione è usare un trigger composto, che assomiglierebbe a:
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;
Un trigger composto consente ogni punto temporale (BEFORE STATEMENT
, BEFORE ROW
, AFTER ROW
e AFTER STATEMENT
) da trattare. Si noti che i punti temporali vengono sempre richiamati nell'ordine indicato. Quando un'istruzione SQL appropriata (ad esempio INSERT INTO TABLE1
o DELETE FROM TABLE1
) viene eseguito e questo trigger viene attivato, il primo punto temporale da invocare sarà BEFORE STATEMENT
, e il codice nel BEFORE STATEMENT
il gestore assegnerà una tabella PL/SQL per contenere un gruppo di numeri. In questo caso i numeri da memorizzare nella tabella PL/SQL saranno i valori TABLE2_ID da TABLE1. (Una tabella PL/SQL viene utilizzata al posto, ad esempio, di un array perché una tabella può contenere un numero variabile di valori, mentre se utilizzassimo un array dovremmo sapere in anticipo quanti numeri dovremmo memorizzare. Non possiamo sapere in anticipo quante righe saranno interessate da una particolare istruzione, quindi utilizziamo una tabella PL/SQL).
Quando il AFTER EACH ROW
viene raggiunto il punto temporale e scopriamo che l'istruzione in elaborazione è un INSERT, il trigger va semplicemente avanti ed esegue l'AGGIORNAMENTO necessario a TABLE2 poiché ciò non causerà problemi. Tuttavia, se viene eseguito un DELETE, il trigger salva TABLE1.TABLE2_ID nella tabella PL/SQL allocata in precedenza. Quando il AFTER STATEMENT
viene finalmente raggiunto il punto temporale, la tabella PL/SQL allocata in precedenza viene ripetuta e per ogni TABLE2_ID trovato viene eseguito l'aggiornamento appropriato.
Documentazione qui.