表正在变异,触发器/函数可能看不到它(阻止平均成绩降至 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] 正在变异,触发器/函数可能看不到它

表正在变异,触发器/函数可能看不到它/ORA-04088: 执行触发器时出错

Oracle - 在没有触发器的情况下更新表时出现“表正在变异,触发器/函数可能看不到它”错误