触发计数行给出ORA-04091:表是变异的,触发器/函数可能看不到它

Arn*_* C. 1 sql oracle plsql database-trigger sql-update

更新我的表时出现此错误

 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'
Run Code Online (Sandbox Code Playgroud)

保存对表"Username"的更改时出错."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;
Run Code Online (Sandbox Code Playgroud)

Tho*_*ner 5

Oracle在此试图告诉您的是:

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

例如:

update empproject set startdate = startdate + 100, enddate = enddate + 100;
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

这里描述了Compund触发器:https://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#LNPLS2005