如何修复触发器中的变异 table 错误?
How to fix mutating table error in trigger?
我有两个 table,名字分别是 item
和 stock_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;
我有两个 table,名字分别是 item
和 stock_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;