sql >> Base de Datos >  >> RDS >> Oracle

Desencadenador de Oracle después de insertar o eliminar

Lo que te has encontrado es la clásica excepción de "tabla mutante". En un disparador ROW, Oracle no le permite ejecutar una consulta en la tabla en la que está definido el disparador, por lo que es SELECT contra TABLE1 en el DELETING parte del desencadenante que está causando este problema.

Hay un par de maneras de evitar esto. Quizás lo mejor en esta situación es usar un activador compuesto, que se vería así:

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 disparador compuesto permite que cada punto de tiempo (BEFORE STATEMENT , BEFORE ROW , AFTER ROW y AFTER STATEMENT ) ser manejado. Tenga en cuenta que los puntos de tiempo siempre se invocan en el orden dado. Cuando una instrucción SQL adecuada (es decir, INSERT INTO TABLE1 o DELETE FROM TABLE1 ) se ejecuta y se dispara este activador, el primer punto de tiempo que se invocará será BEFORE STATEMENT , y el código en BEFORE STATEMENT handler asignará una tabla PL/SQL para contener un montón de números. En este caso, los números que se almacenarán en la tabla PL/SQL serán los valores de TABLE2_ID de TABLE1. (Se usa una tabla PL/SQL en lugar de, por ejemplo, una matriz porque una tabla puede contener una cantidad variable de valores, mientras que si usáramos una matriz tendríamos que saber de antemano cuántos números necesitaríamos almacenar. No podemos saber de antemano cuántas filas se verán afectadas por una declaración en particular, por lo que usamos una tabla PL/SQL).

Cuando AFTER EACH ROW se alcanza el punto de tiempo y encontramos que la declaración que se está procesando es un INSERTAR, el activador simplemente continúa y realiza la ACTUALIZACIÓN necesaria en TABLE2 ya que esto no causará ningún problema. Sin embargo, si se está realizando una ELIMINACIÓN, el activador guarda TABLE1.TABLE2_ID en la tabla PL/SQL asignada anteriormente. Cuando el AFTER STATEMENT finalmente se alcanza el punto de tiempo, se itera la tabla PL/SQL asignada anteriormente, y para cada TABLE2_ID encontrado se realiza la actualización adecuada.

Documentación aquí.