考虑下表
create table Test1
(
id number(5),
batch number(5),
value number(10)
);
Insert into test1 values(1,10,200);
Insert into test1 values(2,10,700);
Insert into test1 values(3,10,400);
Insert into test1 values(1,20,1000);
Insert into test1 values(2,20,100);
Insert into test1 values(3,20,5000);
commit;
现在,这里的要求是每批只保留1行并添加批处理的所有值,并将其分配给要保留的Id(此处选择的最小ID)。应删除批处理的剩余行。只要为批处理插入行,就会发生这种情况。
预期结果
Id Batch Value
1 10 1300
1 20 1600
现有系统有一个逻辑,其中Test1上有After Insert或Update Trigger,它通过dbms_job调用Package。在此软件包中,更新和删除发生。
Create or replace trigger Trigger_Test_1
After Insert or Update of Value on Test_1
For Each row
Begin
...
dbms_job(call to package Pkg_Test_1.Proc_1(:New.Id, :New.Batch));
...
End;
Create or Replace Package Body Pkg_Test_1
As
Procedure Proc_Test_1(p_id number, p_batch number) Is
Begin
Select Min(Id), Batch, sum(Value)
Into v_id, v_batch, value_sum
From Test1
Where Batch = p_batch
Group by Batch
Having count(*) > 1;
If (v_id is not null) then
update test1
set value = value_sum
where id = v_id;
delete from test1
where id <> v_id
and batch = p_batch;
commit;
End If;
End;
End;
问题出在更新期间,其中发生另一次对Trigger(Trigger_Test_1)的调用,导致值在无限循环中更新。最后我不得不删除记录以停止更新。 这是一个尴尬的情况,我知道在触发器中编写这样的逻辑是不值得推荐的,但这是我们系统中现有的逻辑。
有关如何改进代码或如何以不同方式实现此结果的任何想法?
答案 0 :(得分:1)
你能够在触发器中编写它的唯一方法是使用INSTEAD OF触发器,触发器动作,但根本不执行该动作。如果你没有,你将得到 ORA-04091:表A正在变异,触发器/功能可能看不到它,这是因为你正在尝试修改当前正在进行的表在同一会话中被修改。
您试图通过DBMS_JOB
(note that DBMS_JOB
is deprecated in 10g in favour of DBMS_SCHEDULER
)异步执行更新。这也不会真正起作用 - 你可以遇到并发问题并且你没有解决基本问题 - 人们正在将数据插入到为其他东西设计的表中。
你的问题的答案是改变对表的期望。您只能 这样做才能为列BATCH
添加唯一键。从你所说的,这是你的自然钥匙。应用程序应该期望在与此表交互时只有一行,并且应该考虑到这一点。
显然,这并不能解决所有遗留应用程序的问题。但是,也有答案。
MERGE
statement。类似的东西:
rename test1 to real_test1;
alter table test1 add constraint uk_test1 unique (batch);
create or replace view test1 as select * from real_test1;
create or replace trigger tr_test1
instead of insert on test1
for each row
declare
merge into real_test1 o
using ( select :new.batch as batch
, :new.value as value
from dual ) n
on (o.batch = n.batch)
when matched then
update
set o.value = o.value + n.value
when not matched then
insert (o.batch, o.value)
values (n.batch, n.value);
end tr_test;
这意味着:
更重要的是,它会告诉您的用户此表的用途,并开始对其进行正确使用培训。如果可以,请记录仍在尝试插入行的人,并让他们开始合并。
P.S。请不要提交包裹......
答案 1 :(得分:0)
您的更新应使用整个密钥(批次,ID)。目前,您的DBMS作业将更新多行。
update test1
set value = value_sum
where id = v_id;
变为
update test1
set value = value_sum
where batch = v_batch
and id = v_id;
在插入(而不是行级触发器)之后,可能有更直接的方法使用语句级别触发器。您需要阅读一下以确定这是否有效。
您也可以在插入后运行触发器,但不能在更新后运行。更新不会向表中添加新行,因此不需要合并行。