您遇到的是经典的“变异表”异常。在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;
的化合物,触发允许每个定时点(BEFORE STATEMENT,BEFORE ROW,AFTER ROW,和AFTER STATEMENT),以进行处理。请注意,始终按给定的顺序调用时间点。当执行适当的SQL语句(即INSERT INTO TABLE1或DELETE FROM TABLE1)并触发此触发器时,将调用的第一个时间点BEFORE STATEMENT,以及BEFORE STATEMENThandler将分配一个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执行适当的更新。