AFTER UPDATE 触发器上的变异表错误

Posted

技术标签:

【中文标题】AFTER UPDATE 触发器上的变异表错误【英文标题】:Mutating table error on AFTER UPDATE trigger 【发布时间】:2015-09-23 14:52:01 【问题描述】:

根据Burleson,我们可以通过使用 AFTER UPDATE 来避免变异表/触发器错误:

如果您必须使用触发器,最好使用“after”触发器来避免变异表错误,从而避免与变异表相关的货币问题。例如,使用触发器“:after update on xxx”,原来的更新已经完成,表不会发生变异。

但是,SO 的此发帖人使用的是 AFTER UPDATE 并且仍然收到 mutating table 错误。

我花了很多时间阅读各种帖子,并看到了各种解决方案和意见。我之前在自己的代码中使用 AFTER UPDATE 触发器多次看到此错误 - 那么我是如何误解 Burleson 所指的语法的呢?

CREATE OR REPLACE TRIGGER transcript_after_update
AFTER UPDATE on blatChildren
FOR EACH ROW
BEGIN
    update BLATranscript SET (enrollmentStatus, completionDate) = (select 'C',sysdate from dual)
    where id in (select blat.id from BLATranscript blat 
    inner join BlendedActivity bla on blat.blendedactivityid=bla.id
    where blat.id=:new.blaTranscriptId and minCompletion<=(
    select count(countForCompletion) as total from blatChildren blac
    inner join BlendedActivityMembership bam on blac.childActivityId=bam.childActivityId
    where completionDate>=sysdate-acceptPrevWork
    and blat.id=:new.blaTranscriptId));
END; 

我已经阅读了关于 SO 和 asktom 的各种帖子和意见,关于糟糕的编程、规范化、使它们自主(或者为什么不应该)以及为什么不应该为此使用触发器等等,我我对我如何错误地解释 Burleson 或者他的陈述是否不正确更感兴趣。在我看来,更新完成后数据的状态应该是稳定的。

【问题讨论】:

您的误解是:Burleson 不清楚他所说的是 AFTER UPDATE STATEMENT TRIGGER。这与您的更新后行触发器不同。在所有行都被更新后,语句触发器被触发一次。与触发 n 次的行触发器相反,对于受更新影响的每一行触发一次。在 Statement-trigger 中,您没有指定 'For each row',并且您无权访问 ':old' 和 ':new'。 @Njal - 感谢您非常清晰的解释。现在这是有道理的。所以,我想这意味着没有办法使用基本的触发器机制来查询你的触发器在行级别附加到的表,而不是使其自治或其他一些机制,如过程。谢谢你的澄清。 @Njal 虽然接受的答案是相似的,但您的评论是第一位的,并且是一个更好的答案,至少对我而言。 【参考方案1】:

语句级触发器(不是针对每一行)将允许您查询基表而不会出现变异表错误。 但在这种情况下,您将无法使用 :old.:new. 变量,因此对您没有多大帮助。 可能的出路是使用 2 个触发器: - 将 ID-s 存储到临时表的行级触发器, - 语句级触发器基于此临时表运行更新。

【讨论】:

以上是关于AFTER UPDATE 触发器上的变异表错误的主要内容,如果未能解决你的问题,请参考以下文章

在AFTER UPDATE触发器上尝试更新只读列错误

INSTEAD OF与AFTER触发器

SQLite 创建 AFTER UPDATE 触发器

Oracle触发器-变异表触发器不能访问本表

更新前Oracle SQL变异表触发器

如果 BEFORE 触发器产生错误,UPDATE 是不是仍然执行?