核心思路就是:在第一次插入时保存值到包变量中,第二次插入时不再读取表本身,转而读取包变量,可以成功解决这类难题。
在工作中,需要为各个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;
/
本文介绍了一种在Oracle数据库中为不同BOM的ITEM自动分配唯一序号的方法。通过使用包变量跟踪最大序号,避免了并发插入时的ORA-04091错误,并确保了序号的正确递增。

被折叠的 条评论
为什么被折叠?



