Oracle
 sql >> Database >  >> RDS >> Oracle

Trigger Oracle dopo l'inserimento o l'eliminazione

Quello in cui ti sei imbattuto è la classica eccezione "tabella mutante". In un trigger ROW Oracle non ti consente di eseguire una query sulla tabella su cui è definito il trigger, quindi è il SELECT contro TABLE1 nel DELETING parte del trigger che causa questo problema.

Ci sono un paio di modi per aggirare questo problema. Forse la cosa migliore in questa situazione è usare un trigger composto, che assomiglierebbe a:

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 trigger composto consente ogni punto temporale (BEFORE STATEMENT , BEFORE ROW , AFTER ROW e AFTER STATEMENT ) da trattare. Si noti che i punti temporali vengono sempre richiamati nell'ordine indicato. Quando un'istruzione SQL appropriata (ad esempio INSERT INTO TABLE1 o DELETE FROM TABLE1 ) viene eseguito e questo trigger viene attivato, il primo punto temporale da invocare sarà BEFORE STATEMENT , e il codice nel BEFORE STATEMENT il gestore assegnerà una tabella PL/SQL per contenere un gruppo di numeri. In questo caso i numeri da memorizzare nella tabella PL/SQL saranno i valori TABLE2_ID da TABLE1. (Una tabella PL/SQL viene utilizzata al posto, ad esempio, di un array perché una tabella può contenere un numero variabile di valori, mentre se utilizzassimo un array dovremmo sapere in anticipo quanti numeri dovremmo memorizzare. Non possiamo sapere in anticipo quante righe saranno interessate da una particolare istruzione, quindi utilizziamo una tabella PL/SQL).

Quando il AFTER EACH ROW viene raggiunto il punto temporale e scopriamo che l'istruzione in elaborazione è un INSERT, il trigger va semplicemente avanti ed esegue l'AGGIORNAMENTO necessario a TABLE2 poiché ciò non causerà problemi. Tuttavia, se viene eseguito un DELETE, il trigger salva TABLE1.TABLE2_ID nella tabella PL/SQL allocata in precedenza. Quando il AFTER STATEMENT viene finalmente raggiunto il punto temporale, la tabella PL/SQL allocata in precedenza viene ripetuta e per ogni TABLE2_ID trovato viene eseguito l'aggiornamento appropriato.

Documentazione qui.