Ce que vous avez rencontré est l'exception classique de "table de mutation". Dans un déclencheur ROW, Oracle ne vous permet pas d'exécuter une requête sur la table sur laquelle le déclencheur est défini - c'est donc le SELECT
contre TABLE1 dans le DELETING
partie du déclencheur à l'origine de ce problème.
Il existe plusieurs façons de contourner ce problème. Le mieux dans cette situation est peut-être d'utiliser un déclencheur composé, qui ressemblerait à :
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 déclencheur composé permet à chaque point de synchronisation (BEFORE STATEMENT
, BEFORE ROW
, AFTER ROW
, et AFTER STATEMENT
) A prendre en charge. Notez que les points de synchronisation sont toujours invoqués dans l'ordre indiqué. Lorsqu'une instruction SQL appropriée (c'est-à-dire INSERT INTO TABLE1
ou DELETE FROM TABLE1
) est exécuté et ce déclencheur est déclenché, le premier point de synchronisation à invoquer sera BEFORE STATEMENT
, et le code dans BEFORE STATEMENT
le gestionnaire allouera une table PL/SQL pour contenir un tas de nombres. Dans ce cas, les nombres à stocker dans la table PL/SQL seront les valeurs TABLE2_ID de TABLE1. (Une table PL/SQL est utilisée à la place, par exemple, d'un tableau car une table peut contenir un nombre variable de valeurs, alors que si nous utilisions un tableau, nous devrions savoir à l'avance combien de nombres nous aurions besoin de stocker. Nous ne pouvons pas savoir à l'avance combien de lignes seront affectées par une instruction particulière, nous utilisons donc une table PL/SQL).
Lorsque le AFTER EACH ROW
point de synchronisation est atteint et nous constatons que l'instruction en cours de traitement est un INSERT, le déclencheur continue simplement et effectue la MISE À JOUR nécessaire sur TABLE2 car cela ne posera pas de problème. Cependant, si un DELETE est en cours d'exécution, le déclencheur enregistre le TABLE1.TABLE2_ID dans la table PL/SQL allouée précédemment. Lorsque le AFTER STATEMENT
moment est finalement atteint, la table PL/SQL allouée précédemment est itérée et pour chaque TABLE2_ID trouvé, la mise à jour appropriée est effectuée.
Documentation ici.