我有三张桌子:A和B. 关系是A可以有很多B 所以B有一个对A.id作为其列之一的引用
Table A
|id|date|...
Table B
|id|A_id|...
我在表A上创建了一个Oracle触发器,以便在更新时更新A_Mod表。
此触发器是
CREATE OR REPLACE TRIGGER TR_A_INSERT_UPDATE
AFTER INSERT OR UPDATE ON A
FOR EACH ROW
BEGIN
INSERT INTO A_Mod values(..., :new.date, ...)
END;
这很好用:))
我的问题是为表B创建触发器。
触发器是:
CREATE OR REPLACE TRIGGER TR_B_INSERT_UPDATE
AFTER INSERT OR UPDATE ON B
FOR EACH ROW
DECLARE
ts TIMESTAMP;
BEGIN
SELECT aa.date INTO ts FROM B bb
INNER JOIN A aa ON a.id = bb.A_id
WHERE bb.id = :new.id;
INSERT INTO A_Mod values(..., :new.date, ...)
END;
此触发器正在读取表B中更新行的ID,然后从表A中的相应行获取日期。然后尝试将其插入A_Mod
问题是我收到了一个变异错误
Error report:
SQL Error: ORA-04091: table B is mutating, trigger/function may not see it
ORA-06512: at "TR_B_INSERT_UPDATE", line 5
ORA-04088: error during execution of trigger 'TR_B_INSERT_UPDATE'
04091. 00000 - "table %s.%s is mutating, trigger/function may not see it"
*Cause: A trigger (or a user defined plsql function that is referenced in
this statement) attempted to look at (or modify) a table that was
in the middle of being modified by the statement which fired it.
*Action: Rewrite the trigger (or function) so it does not read that table.
查看docs我可以通过删除FOR EACH ROW
行并在每个语句触发一次触发器而不是每行一次来删除此错误。不幸的是我使用的是ORM映射器,所以不要控制更新的发生方式。我认为有时候更新可能涵盖多行。
文档说了一些关于创建临时表的内容,但我不确定这会有什么帮助。我是否必须在触发器内创建一个临时表,在此临时表上创建一个触发器,然后更新A_Mod,在触发器触发时更新此临时表,然后删除所有内容?
任何提示都非常感谢。
由于
答案 0 :(得分:2)
B
的触发器中没有任何理由可以查询B
表。您应该可以使用A
:new.a_id
CREATE OR REPLACE TRIGGER TR_B_INSERT_UPDATE
AFTER INSERT OR UPDATE ON B
FOR EACH ROW
DECLARE
ts TIMESTAMP;
BEGIN
SELECT a.date
INTO ts
FROM A a
WHERE a.id = :new.a_id;
INSERT INTO A_Mod values(..., :new.date, ...)
END;
但是,从数据建模的角度来看,如果子表上的触发器需要查询父表中的任何信息或将数据插入父表写入的同一历史表中,我将非常担心。这似乎可能表明正常化问题。