Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Gatilho Oracle após inserir ou excluir


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.