如何修复触发器中的变异 table 错误?

How to fix mutating table error in trigger?

我有两个 table,名字分别是 itemstock_item。 当我更新 item table 时,应该会触发名称为 beforeItem 的触发器,它会从 stock_qty 中减去新更新的 qty。但它抛出

ORA-04091: table **** is mutating trigger/function may not see it

我该如何解决这个问题?

我的tables:

create table stock_item
(no number primary key,itemName varchar2(10),stock_Qty number);

create table item 
(no number,Name varchar2(10),qty number);   

我的触发器:

create or replace trigger beforeItem
before update on item
for each row 
declare 
chk_no number;
chk_item varchar2(10);
chk_qty number;
--pragma AUTONOMOUS_TRANSACTION;
-- this code will skip the update code.
begin
select no,name,qty into chk_no, chk_item,chk_qty from item where  no=:new.no 
and name=:new.name;
update stock_item set itemName = itemName - chk_qty where no=chk_no and 
itemName=chk_item; 
--commit;
end; 

当触发器针对拥有触发器的 table 发出 DML 时,Oracle 抛出 ORA-04091;这包括 SELECT 语句。原因很简单:table 的状态未知,因为触发器在事务 期间触发,因此触发器的 DML 的结果是不可预测的table。

解决方案通常很简单:删除 DML。这肯定是这里的答案,因为您的 :NEW 记录具有在 stock_item:

上执行更新所需的所有值
create or replace trigger beforeItem
    before update on item
    for each row 
 begin

    update stock_item si
    set si.stock_Qty = si.stock_Qty - :new.qty 
    where si.no = :new.no; 

end; 

but the stock_item table don't know what is the current value of the item table qty to subtract from.

好的,所以你的意思是,你想用旧(当前)ITEM.QTY 和新 差异 来更新 STOCK_ITEM.QTY (更新)值。那将是这样的:

create or replace trigger beforeItem
    before update on item
    for each row 
 begin

    update stock_item si
    set si.stock_Qty = si.stock_Qty - ( (nvl(:old.qty,0) - nvl(:new.qty,0)) )
    where si.no = :new.no; 

end; 

这是我在 SQL Fiddle 上的解决方案演示。


顺便说一下,请注意我已经更正了您的更新声明:从库存名称中减去项目数量确实没有意义。另外,当有主键可以使用时,WHERE子句中不需要使用itemName

您不能在此触发器中引用 table ITEM,因为它会导致您的错误。而不是使用 SELECT 语句使用 new/old 参数。试试这个版本的触发器。

create or replace trigger beforeItem
  before update on item
  for each row 
begin
  -- if :new.qty is not null then
    update stock_item set 
      -- logic to maintaint if no changes on qty field were done
      stock_Qty = stock_Qty - ( nvl(:new.qty,0) - nvl(:old.qty,0) ) 
      where no=:new.no and itemName=:new.name; 
  -- end if;
end;