-1
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.
4

2 回答 2

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)在表 score 中的组合是唯一的。如果不是这种情况,并且您需要在其中插入行,则deleted_scores需要使用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;
/

有关“table is mutating”限制的更多细节可以在手册中找到:

http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS759

于 2016-02-23T07:48:15.447 回答
0

切勿在 DML 触发器之前使用。在某些情况下,触发器可以为同一行多次触发。当您的会话被其他会话更新/删除同一行阻止时,可能会发生这种情况。

使用复合触发器并在触发器会话后应用 DML 更改。那么你就可以确定,你看到的数据真的是正确的。

变异表错误意味着Oracle 无法保证您所做的事情是确定性的。

于 2016-02-23T07:57:36.070 回答