Oracle
 sql >> Base de données >  >> RDS >> Oracle

Déclencheur Oracle après insertion ou suppression

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.