表都在变异,触发/功能可能看不到它(下降到低于2.5停止,平均品位)

问题描述:

这里的问题:表都在变异,触发/功能可能看不到它(下降到低于2.5停止,平均品位)

创建触发器,以防止任何变化回吐关系会下降的总体平均品位在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。

我认为你可以通过在触发之前将其重写为而不是触发之后的来解决此问题。但是,这对于插入和删除可能有点复杂。我们的想法是:

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; 
+0

谢谢你的回应。我试过这个,并得到相同的错误。我想知道我的成绩平均值是否正确计算,如果我需要做一些“elsif”声明,如果它在2.5以上,继续更新/插入/删除。我迷路了。大声笑 – 2013-04-24 02:52:18

+1

好吧,这是我推断出的。当表可以更改时,您不能在pl/sql内部为触发器执行选择查询。我不知道我是否可以在不使用pl/sql的情况下重写。 – 2013-04-24 03:27:47

+1

@TheRationalist。 。 。作为惯例,我将插入/更新/删除包装到存储的程序中,而不是依赖触发器。我认为问题是触发器中的*之后(我在脑海中改变了它,而不是在代码中)。我也改变了逻辑,所以它会在'update'的情况下工作 - 你可能在删除时得到一个除0,所以实际的逻辑有点复杂。 – 2013-04-24 13:16:37

首先,你需要了解触发器,变异表错误和复合触发器:http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS2005

你的触发更新后或插入或删除。意思是如果你在这个表上运行UPDATE或INSERT或DELETE语句,触发器将触发。但是你试图在你的触发器里再次更新同一个表,这是compl。错误。这就是你得到错误的原因。你不能修改触发器正在触发的同一个表格。触发器的目的是在表格更新,插入或删除时自动触发。你需要的是一些程序,而不是触发器。

我有同样的问题,我注意到,如果你在同一张桌子上做选择,你把触发器放在你身上可能会遇到这个问题。 您可以删除FOR EACH ROW 或使用中的数据:新建进行计算(如果可能),然后进行更新。

在你的情况下,使用单独的表格来获得每学期的avg_grade会更有意义。

即使我们在我们的项目中也遇到同样的问题。但在几个oracle论坛搜索后,我们找到了下面的解决方案。

1)将旧/新列数据保存为临时表中的行级别触发器。 2)写一个语句级别的触发器,并使用步骤1中保存的数据。

这将解决我认为的问题。

DECLARE里面使用这个声明,它会起作用。

pragma autonomous_transaction; 
+0

您的解决方案适用于我。对于通过相同问题的其他人理解为什么会发生这种情况: ORA-04091 原因:触发器(或在此语句中引用的用户定义的PL/SQL函数)试图查看(或修改)该表正处于被解雇的陈述中。 操作:重写触发器(或函数),使其不读取该表。 来源:http://docs.oracle.com/cd/B10501_01/server.920/a96525/e2100.htm#1002387 – Arthur 2016-10-17 12:45:41