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í.