首页 > 解决方案 > 触发器计数行给出 ORA-04091:表正在变异,触发器/函数可能看不到它

问题描述

更新表格时出现此错误

 UPDATE "Username"."EMPPROJECT" 
 SET ENDDATE = TO_DATE('2016-09-11 00:00:00', 'YYYY-MM-DD HH24:MI:SS') 
 WHERE ROWID = 'AAF+8XAAEAAGBIGAAB'
   AND ORA_ROWSCN = '537035617'

保存对表“用户名”的更改时出错。“EMPPROJECT”:第 8 行:
ORA-04091:表 Username.EMPPROJECT 正在变异,触发器/函数可能看不到它 ORA-06512:在“Username.CHECKOVERLAPDATEUPDATE”,第 4 行
ORA-04088 :执行触发器“Username.CHECKOVERLAPDATEUPDATE”时出错

触发器定义如下:

create or replace trigger checkOverlapDateUpdate 
before Update on EmpProject
for each row
declare
    countOfOverlap integer;
begin
    select count(*) 
    into countOfOverlap 
    from EmpProject EP 
    where isOverlapping(:new.startDate, :new.endDate, startDate, endDate) = 1 
      and EP.EmpID = :new.EmpId;

    if countOfOverlap > 0 
    then
        RAISE_APPLICATION_ERROR(-00000, 'Overlapping Insertion Dates');
        rollback;
    end if;
end;

标签: sqloracleplsqlsql-updatedatabase-trigger

解决方案


Oracle 在这里试图告诉你的是:

  1. 更新可以跨越多行。
  2. 但是,您的触发器会查看单行更新 ( for each row)。
  3. 由于多行更新语句的单行更新以任意顺序发生,Oracle 无法保证与触发器中的子查询一致、确定的结果。

例如:

update empproject set startdate = startdate + 100, enddate = enddate + 100;

应该不成问题,因为所有日期范围都会转移相同的天数。但是,在更新第一行时,单独看这个,可能会发生冲突,因为其他行还没有改变。表中甚至可能只有两行中间情况会导致日期范围重叠。然后它会突然取决于 Oracle 为更新首先选择的两行中的哪一行。在一种情况下,更新运行时不会出错,在另一种情况下,它会崩溃。一个不确定的结果,这当然是不允许的。

解决方案是在完成更新后检查行。

您可以编写一个AFTER UPDATE语句触发器(即AFTER UPDATE ON EmpProject没有FOR EACH ROW)并将表的每一行与所有其他行进行比较。在一张大桌子上,这可能非常昂贵。

首选方法是编写复合触发器。此触发器将有一个用于更改行(或其键)的数组。在触发器AFTER EACH ROW部分,您需要将行添加到数组中,在触发器AFTER STATEMENT部分,您将检查每一行的重叠范围。

此处描述了复合触发器: https ://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#LNPLS2005


推荐阅读