插入或删除后的Oracle触发器
对不起我的英语 我有两张桌子:插入或删除后的Oracle触发器,oracle,triggers,oracle11g,Oracle,Triggers,Oracle11g,对不起我的英语 我有两张桌子: Table1 id table2_id num modification_date 及 我想做一个触发器,在表1中插入或删除后更新表2.table1lastnum中的最后一个值num 我的触发器: CREATE OR REPLACE TRIGGER TABLE1_NUM_TRG AFTER INSERT OR DELETE ON table1 FOR EACH ROW BEGIN IF INSERTING then UPDATE table2
Table1
id
table2_id
num
modification_date
及
我想做一个触发器,在表1中插入或删除后更新表2.table1lastnum中的最后一个值num
我的触发器:
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;
我做错了什么?您遇到的是典型的变异表异常。在一行中,触发器Oracle不允许您对定义了触发器的表运行查询,因此导致此问题的是触发器的删除部分中的“针对表1选择” 有几种方法可以解决这个问题。在这种情况下,最好使用复合触发器,它看起来像:
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;
复合触发器允许处理语句前、行前、行后和语句后的每个计时点。请注意,定时点总是按照给定的顺序调用。当执行适当的SQL语句(即INSERT INTO TABLE1或DELETE FROM TABLE1)并触发此触发器时,要调用的第一个计时点将是BEFORE语句,BEFORE语句处理程序中的代码将分配一个PL/SQL表来保存一组数字。在这种情况下,PL/SQL表中存储的数字将是表1中的表2_ID值。例如,使用PL/SQL表而不是数组,因为表可以容纳不同数量的值,而如果使用数组,则必须事先知道需要存储多少个数字。我们无法预先知道有多少行会受到特定语句的影响,所以我们使用PL/SQL表
当到达AFTER EACH ROW计时点并且我们发现正在处理的语句是INSERT时,触发器将继续执行并对TABLE2执行必要的更新,因为这不会导致问题。但是,如果正在执行删除,则触发器会将TABLE1.TABLE2_ID保存到先前分配的PL/SQL表中。当最终到达AFTER语句计时点时,将遍历先前分配的PL/SQL表,并对找到的每个TABLE2\u ID执行适当的更新
.您必须为delete定义一个before触发器。请尝试使用两个触发器
CREATE OR REPLACE TRIGGER INS_TABLE1_NUM_TRG
AFTER INSERT ON table1
FOR EACH ROW
BEGIN
UPDATE table2
SET table2num = :new.num
WHERE table2.id = :new.table2_id;
END INS_TABLE1_NUM_TRG;
CREATE OR REPLACE TRIGGER DEL_TABLE1_NUM_TRG
BEFORE DELETE ON table1
FOR EACH ROW
BEGIN
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 DEL_TABLE1_NUM_TRG;
@psaraj12答案是最好的IMHO,但在DELETE触发器中,我会使用:OLD符号,因为内部查询是不必要的,并且会显著降低触发器的速度:
...
BEFORE DELETE ON table1
FOR EACH ROW
UPDATE table2
SET table2num = :OLD.num
WHERE table2.id = :OLD.table2_id;
...
您试图在删除一行的表上运行SELECT语句。用谷歌搜索错误代码ORA-04091,您会得到很多答案。
CREATE OR REPLACE TRIGGER INS_TABLE1_NUM_TRG
AFTER INSERT ON table1
FOR EACH ROW
BEGIN
UPDATE table2
SET table2num = :new.num
WHERE table2.id = :new.table2_id;
END INS_TABLE1_NUM_TRG;
CREATE OR REPLACE TRIGGER DEL_TABLE1_NUM_TRG
BEFORE DELETE ON table1
FOR EACH ROW
BEGIN
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 DEL_TABLE1_NUM_TRG;
...
BEFORE DELETE ON table1
FOR EACH ROW
UPDATE table2
SET table2num = :OLD.num
WHERE table2.id = :OLD.table2_id;
...