`
javasee
  • 浏览: 922869 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

解决ORA-04091行触发器中访问变异表的难题

阅读更多

核心思路就是:在第一次插入时保存值到包变量中,第二次插入时不再读取表本身,转而读取包变量,可以成功解决这类难题。

在工作中,需要为各个BOM的每个ITEM依次自动编号,不同BOM的ITEM的SEQ_NUMBER列都要 按1.2.3…自动生成序号。
第一次,我这样写,

CREATE   OR   REPLACE   TRIGGER  TR_BOM_AUTONUMBER_SEQNUMBER
BEFORE 
INSERT
ON  BOM 
REFERENCING NEW 
AS  NEW OLD  AS  OLD
FOR  EACH ROW
DECLARE
    tmpVar 
number ;
BEGIN
    tmpVar :
=   0 ;
   
SELECT  GREATEST(nvl( Max (to_number(SEQ_NUMBER)), 0 ),  Count ( * ))  +   1   INTO  tmpVar  FROM  BOM  Where  BOMID = :New.BOMID;
   :NEW. SEQ_NUMBER:
=  nvl(tmpVar, 1 );
END  TR_BOM_AUTONUMBER_SEQNUMBER;

由于Insert操作会修改表数据,所以Insert…Select插入多行数据时,会报ORA- 04091: table string.string is mutating, trigger/function may not see it错误,原因在于插入第2条数据时表已修改不能再访问。

查阅了很多文章,有提示在其中使用 PRAGMA AUTONOMOUS_TRANSACTION来保证每行插入动作为自治事务。但实际上,经过我的测试,虽然DML不会出错,但实际SEQ_NUMBER全部为1,没有达到依次自动编号的目的。

经过多次试验后,我使用保存于包中的索引表保存各个BOM的最大SEQ_NUMBER,可以防止BOM之间及用户之间的并发冲突。

核心思路就是:在第一次插入时保存值到包变量中,第二次插入时不再读取表本身,转而读取包变量,可以成功解决这类难题。

详细代码如下:

CREATE   OR   REPLACE  PACKAGE BOM_AUTONUMBER
IS
TYPE t_MAX_SEQNUMBER 
is   table   of   number   INDEX   BY  PLS_INTEGER;
v_MAX_SEQNUMBER t_MAX_SEQNUMBER;
end  BOM_AUTONUMBER;
/
CREATE   OR   REPLACE   TRIGGER  TR_BOM_AUTONUMBER_SEQNUMBER
BEFORE 
INSERT
ON  BOM 
REFERENCING NEW 
AS  NEW OLD  AS  OLD
FOR  EACH ROW
DECLARE
    vNumber 
number ;
    vBOMID 
number ;
BEGIN
    vNumber:
=   0 ;
    vBOMID:
=  :New.BOMID;
    
if   not  BOM_AUTONUMBER.v_MAX_SEQNUMBER. EXISTS (vBOMID)  then
        
SELECT  GREATEST(nvl( Max (to_number(SEQ_NUMBER)), 0 ),  Count ( * ))  INTO  vNumber  FROM  BOM  Where  ITEM  =  vBOMID;
        BOM_AUTONUMBER.v_MAX_SEQNUMBER(vBOMID) :
=  nvl(vNumber,  0 );
    
end   if ;
    BOM_AUTONUMBER.v_MAX_SEQNUMBER(vBOMID) :
=  BOM_AUTONUMBER.v_MAX_SEQNUMBER(vBOMID)  +   1 ;
   :NEW.SEQ_NUMBER :
=  BOM_AUTONUMBER.v_MAX_SEQNUMBER(vBOMID);
END  TR_BOM_ AUTONUMBER_SEQNUMBER;
/
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics