set serveroutput on;
CREATE OR REPLACE TRIGGER hw3
BEFORE DELETE on ENROLLS
for EACH ROW
ENABLE
DECLARE
v_sid number;
v_term varchar2(20);
v_sectno number;
v_COMPNAME varchar2(20);
v_points number;
BEGIN
select :old.SID,:old.TERM,:old.SECTNO into v_sid,v_term,v_sectno from enrolls;
select COMPNAME,points into v_compname,v_points from scores
where scores.sid=v_sid and scores.term=v_term and scores.sectno=v_sectno;
INSERT into DELETED_SCORES (SID,TERM,SECTNO,compname,points)
values (v_sid,v_term,v_sectno,v_compname,v_points);
DELETE FROM SCORES
WHERE SID=V_SID AND TERM=V_TERM AND SECTNO=V_SECTNO;
END;
/
有两个表,即注册和分数。 SCORES表有一个组合的外键,包括SID,TERM和SECTNO,引用表ENROLLS。 触发器现在已成功编译,但是出现如下问题:
Error starting at line : 24 in command -
DELETE FROM enrolls
WHERE SID=1111 and term='F12' and sectno=1031
Error report -
SQL Error: ORA-04091: table C16_HE_JIEL.ENROLLS is mutating, trigger/function may not see it
ORA-06512: at "C16_HE_JIEL.HW3", line 8
ORA-04088: error during execution of trigger 'C16_HE_JIEL.HW3'
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.
答案 0 :(得分:0)
此选择:
select :old.SID,:old.TERM,:old.SECTNO into v_sid,v_term,v_sectno from enrolls;
无效有两个原因:
无论如何都不需要选择,您可以直接使用OLD
记录中的值(并删除无效的select ... from enrolls
)
select COMPNAME,points
into v_compname,v_points
from scores
where scores.sid = :old.SID
and scores.term = :old.TERM
and scores.sectno = :old.SECTNO;
上述声明要求(sid, term, secno)
的组合在表格分数中是唯一的。如果不是这种情况,并且您需要在deleted_scores
中插入多个行,则需要使用基于select的INSERT
。
然后完全消除了变量的需要。因此整个触发器可以简化为:
CREATE OR REPLACE TRIGGER hw3
BEFORE DELETE on ENROLLS
for EACH ROW
BEGIN
INSERT into DELETED_SCORES (SID,TERM,SECTNO,compname,points)
select sid, term, sectno, compname, points
from scores
where scores.sid = :old.SID
and scores.term = :old.TERM
and scores.sectno = :old.SECTNO;
DELETE FROM SCORES
WHERE SID = :OLD.sid
AND TERM = :OLD.term
AND SECTNO = :OLD.sectno;
END;
/
关于"表的更多细节正在改变"限制可以在手册中找到:
http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS759
答案 1 :(得分:0)
永远不会在触发DML之前使用。在某些情况下,触发器可以针对同一行多次触发。当会话被其他会话更新/删除同一行阻止时,可能会发生这种情况。
使用复合触发器并在触发器会话后应用DML更改。 那么你可以肯定,你看到的数据真的是正确的。
变异表错误意味着Oracle无法保证您正在做的事情是确定性的。