插入或删除后的Oracle触发器

zer*_*erg 4 oracle triggers oracle11g

对不起我的英语不好.

我有2张桌子:

Table1
id
table2_id
num
modification_date 
Run Code Online (Sandbox Code Playgroud)

Table2
id
table2num
Run Code Online (Sandbox Code Playgroud)

我想打一个触发器,它插入或删除操作后在Table1更新的最后一个值numTable2.table1lastnum.

我的触发器:

CREATE OR REPLACE TRIGGER TABLE1_NUM_TRG
  AFTER INSERT OR DELETE ON table1
  FOR EACH ROW
BEGIN
  IF INSERTING then

  UPDATE table2
  SET    table2num = :new.num
  WHERE  table2.id = :new.table2_id;

  ELSE

  UPDATE table2
  SET    table2num = (SELECT num FROM  (SELECT num FROM table1 WHERE table2_id = :old.table2_id ORDER BY modification_date DESC) WHERE ROWNUM <= 1)
  WHERE  table2.id = :old.table2_id;

  END IF;

END TABLE1_NUM_TRG; 
Run Code Online (Sandbox Code Playgroud)

但删除后Table1我有错误:

ORA-04091: table BD.TABLE1 is mutating, trigger/function may not see it
ORA-06512: at "BD.TABLE1_NUM_TRG", line 11
ORA-04088: error during execution of trigger 'BD.TABLE1_NUM_TRG'
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?

Bob*_*ica 7

您遇到的是经典的"变异表"异常.在ROW触发器中,Oracle不允许您对定义触发器的表运行查询 - 因此它是导致此问题的触发器部分中的SELECTTABLE1 DELETING.

有几种方法可以解决这个问题.在这种情况下,最好的方法是使用复合触发器,它看起来像:

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;
Run Code Online (Sandbox Code Playgroud)

的化合物,触发允许每个定时点(BEFORE STATEMENT,BEFORE ROW,AFTER ROW,和AFTER STATEMENT),以进行处理.请注意,始终按给定的顺序调用时间点.当执行适当的SQL语句(即INSERT INTO TABLE1DELETE FROM TABLE1)并触发此触发器时,将调用第一个定时点BEFORE STATEMENT,并且BEFORE STATEMENT处理程序中的代码将分配PL/SQL表来保存一堆数字.在这种情况下,要存储在PL/SQL表中的数字将是TABLE1中的TABLE2_ID值.(例如,使用PL/SQL表而不是数组,因为表可以容纳不同数量的值,而如果我们使用数组,我们必须事先知道需要存储多少数字.我们事先不能知道特定语句会影响多少行,因此我们使用PL/SQL表).当AFTER EACH ROW达到时间点并且我们发现正在处理的语句是INSERT时,触发器就会继续执行并对TABLE2执行必要的UPDATE,因为这不会导致问题.但是,如果正在执行DELETE,则触发器将TABLE1.TABLE2_ID保存到先前分配的PL/SQL表中.当AFTER STATEMENT最终到达定时点时,先前分配的PL/SQL表被迭代,并且对于找到的每个TABLE2_ID,执行适当的更新.

文档在这里.

分享和享受.