如何在这里解决变异表错误?

时间:2016-02-23 06:39:28

标签: oracle plsql database-trigger

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.

2 个答案:

答案 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无法保证您正在做的事情是确定性的。