表正在变异,触发器/函数可能看不到它(阻止平均成绩降至 2.5 以下)
Posted
技术标签:
【中文标题】表正在变异,触发器/函数可能看不到它(阻止平均成绩降至 2.5 以下)【英文标题】:Table is mutating, trigger/function may not see it (stopping an average grade from dropping below 2.5) 【发布时间】:2013-04-17 10:04:22 【问题描述】:问题来了:
创建一个触发器,以防止对录取关系进行任何更改,从而将任何特定班级的总体平均成绩降至 2.5 以下。注意:此触发器并非旨在解决任何给定学生的平均 GPA,而是应解决特定班级分配的所有成绩的平均成绩。
这是架构:
Student-schema =(studentnum, name, standing, gpa, major)
Class-schema = (schedulenum, semester, department, classnum, days, time, place, enrollment)
Instructor-schema = (name, department, office)
Teaches-schema = (name, schedulenum, semester)
Taking-schema = (studentnum, schedulenum, semester, grade)
我在使用这些触发器时感觉很糟糕,但这是我的尝试:
CREATE OR REPLACE TRIGGER stopChange
AFTER UPDATE OR INSERT OR DELETE ON taking
REFERENCING OLD AS old
NEW AS new
FOR EACH ROW
DECLARE
grd_avg taking.grade%TYPE;
BEGIN
SELECT AVG(grade)
INTO grd_avg
FROM taking
WHERE studentnum = :new.studentnum
AND schedulenum = :new.schedulenum
AND semester = :new.semester;
IF grd_avg < 2.5 THEN
UPDATE taking
SET grade = :old.grade
WHERE studentnum = :old.studentnum
AND schedulenum = :old.schedulenum
AND semester = :old.semester;
END IF;
END;
/
我显然做错了什么,因为当我去更新或删除一个元组时,我得到了错误:
ERROR at line 1:
ORA-04091: table TAKING is mutating, trigger/function may not see it
ORA-06512: at "STOPCHANGE", line 6
ORA-04088: error during execution of trigger 'STOPCHANGE'
有什么建议吗?我正在使用 Oracle。
【问题讨论】:
【参考方案1】:如果您想通过连接其他表(TABLE_ADDRESS)来检索其他数据。这是我的解决方案。
CREATE OR REPLACE TRIGGER TRIGGER_TABLE_ACTIVITIES AFTER INSERT ON TABLE_NAME
FOR EACH ROW
DECLARE
V_ADDRESS VARCHAR2(100);
BEGIN
SELECT A.ADDRESS INTO V_ADDRESS
FROM TABLE_ADDRESS A
WHERE A.ADDRESSID = :NEW.ADDRESSID
;
INSERT INTO TABLE_ACTIVITIES(
NAME, ADDRESS)
VALUES(:NEW.NAME, V_ADDRESS);
END;
/
【讨论】:
【参考方案2】:在DECLARE
中使用这个语句,它会起作用。
pragma autonomous_transaction;
【讨论】:
您的解决方案对我有用。对于通过相同问题的其他人,以了解为什么会发生这种情况:ORA-04091 原因:触发器(或此语句中引用的用户定义的 PL/SQL 函数)试图查看(或修改)位于被触发它的语句修改的中间。行动:重写触发器(或函数),使其不读取该表。来源:docs.oracle.com/cd/B10501_01/server.920/a96525/… 这将抑制错误,但触发器可能仍然无法像我的情况一样正确运行。 我同意@RaymondWachaga:这将隐藏错误并造成数据不一致。【参考方案3】:即使我们在项目中也遇到了同样的问题。但是在几个oracle论坛搜索后,我们找到了以下解决方案。
1) 将旧/新列数据保存在临时表中,作为行级触发器的一部分。 2) 编写语句级触发器,使用步骤1中保存的数据。
这会解决我认为的问题。
【讨论】:
【参考方案4】:我遇到了同样的问题,我注意到如果您在同一张桌子上进行选择,您可能/将会遇到这个问题。 您可以删除 FOR EACH ROW 或使用 :New 中的数据进行计算(如果可能),然后进行更新。
在您的情况下,使用单独的表格来获得每学期的 avg_grade 会更有意义。
【讨论】:
【参考方案5】:我认为您可以通过将其重写为 before 触发器而不是 after 触发器来解决此问题。但是,这对于插入和删除可能有点复杂。这个想法是:
CREATE OR REPLACE TRIGGER stopChange
BEFORE UPDATE OR INSERT OR DELETE ON taking
REFERENCING OLD AS old
NEW AS new
FOR EACH ROW
DECLARE
grd_avg taking.grade%TYPE;
BEGIN
SELECT (SUM(grade) - oldgrade + new.grade) / count(*)
INTO grd_avg
FROM taking
WHERE studentnum = :new.studentnum
AND schedulenum = :new.schedulenum
AND semester = :new.semester;
IF grd_avg < 2.5 THEN
new.grade = old.grade
END IF;
END;
【讨论】:
感谢您的回复。我试过这个,并得到同样的错误。我想知道我的平均成绩是否计算正确,如果它高于 2.5,我是否需要做一些“elsif”语句来继续更改更新/插入/删除。我迷路了。大声笑 好的,这就是我的推断。当表可以更改时,您不能在 pl/sql 内为触发器执行选择查询。我想知道我是否可以在不使用 pl/sql 的情况下重写它。 @TheRationalist 。 . .作为实践,我将插入/更新/删除包装到存储过程中,而不是依赖触发器。我认为问题是触发器中的 after (我改变了主意,但显然没有在代码中)。我还更改了逻辑,因此它可以在update
的情况下工作——您可能会在删除时得到除以 0,因此实际逻辑要复杂一些。
嗨,我现在遇到同样的错误。当我尝试在数据库中插入一个实体对象时,触发器工作正常。但是当我试图插入多行(实体对象)时,它会抛出上述错误“表正在变异,触发器/函数可能看不到它”。当将新 ID 添加到数据库中时,我们使用触发器仅将一列 IS_LATEST 更新为“Y”或“N”。有人可以帮我吗?【参考方案6】:
首先您需要了解触发器、变异表错误和复合触发器:http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS2005
您的触发器是 AFTER UPDATE OR INSERT OR DELETE。意味着如果您在此表上运行 UPDATE OR INSERT OR DELETE 语句,触发器将触发。但是您正尝试在触发器中再次更新同一个表,这是 compl。错误的。这就是您收到错误的原因。您不能修改触发器正在触发的同一个表。触发器的目的是在您的情况下更新、插入或删除表时自动触发。你需要的是一些程序,而不是触发器。
【讨论】:
切中要害——“你需要的是一些程序,而不是触发器。”以上是关于表正在变异,触发器/函数可能看不到它(阻止平均成绩降至 2.5 以下)的主要内容,如果未能解决你的问题,请参考以下文章
ORA-04091: 表 JOSEP.EMP 正在变异,触发器/函数可能看不到它
ORA-04091: 表 [blah] 正在变异,触发器/函数可能看不到它