O que você encontrou é a clássica exceção de "tabela de mutação". Em um gatilho ROW, o Oracle não permite que você execute uma consulta na tabela na qual o gatilho está definido - portanto, é o
SELECT
contra TABLE1 no DELETING
parte do gatilho que está causando esse problema. Existem algumas maneiras de contornar isso. Talvez o melhor nessa situação seja usar um gatilho composto, que seria algo como:
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;
Um gatilho composto permite que cada ponto de tempo (
BEFORE STATEMENT
, BEFORE ROW
, AFTER ROW
e AFTER STATEMENT
) a ser manuseado. Observe que os pontos de tempo são sempre invocados na ordem dada. Quando uma instrução SQL apropriada (ou seja, INSERT INTO TABLE1
ou DELETE FROM TABLE1
) é executado e este gatilho é acionado, o primeiro ponto de tempo a ser invocado será BEFORE STATEMENT
, e o código na BEFORE STATEMENT
handler irá alocar uma tabela PL/SQL para armazenar um monte de números. Neste caso, os números a serem armazenados na tabela PL/SQL serão os valores TABLE2_ID da TABLE1. (Uma tabela PL/SQL é usada em vez de, por exemplo, um array porque uma tabela pode conter um número variável de valores, enquanto se usássemos um array teríamos que saber antecipadamente quantos números precisaríamos armazenar. Não podemos saber de antemão quantas linhas serão afetadas por uma determinada instrução, então usamos uma tabela PL/SQL). Quando
AFTER EACH ROW
ponto de tempo é atingido e descobrimos que a instrução que está sendo processada é um INSERT, o gatilho apenas segue em frente e executa o UPDATE necessário para TABLE2, pois isso não causará problemas. No entanto, se um DELETE estiver sendo executado, o gatilho salvará o TABLE1.TABLE2_ID na tabela PL/SQL alocada anteriormente. Quando o AFTER STATEMENT
O ponto de tempo é finalmente alcançado, a tabela PL/SQL alocada anteriormente é iterada e, para cada TABLE2_ID encontrado, a atualização apropriada é executada. Documentação aqui.