我有一个PL/SQL函数在Oracle数据库上执行更新/插入操作,该数据库维持目标总数并返回现有值和新值之间的差异。
这里是我到目前为止的代码:使用Oracle和PL/SQL插入或更新
FUNCTION calcTargetTotal(accountId varchar2, newTotal numeric) RETURN number is
oldTotal numeric(20,6);
difference numeric(20,6);
begin
difference := 0;
begin
select value into oldTotal
from target_total
WHERE account_id = accountId
for update of value;
if (oldTotal != newTotal) then
update target_total
set value = newTotal
WHERE account_id = accountId
difference := newTotal - oldTotal;
end if;
exception
when NO_DATA_FOUND then
begin
difference := newTotal;
insert into target_total
(account_id, value)
values
(accountId, newTotal);
-- sometimes a race condition occurs and this stmt fails
-- in those cases try to update again
exception
when DUP_VAL_ON_INDEX then
begin
difference := 0;
select value into oldTotal
from target_total
WHERE account_id = accountId
for update of value;
if (oldTotal != newTotal) then
update target_total
set value = newTotal
WHERE account_id = accountId
difference := newTotal - oldTotal;
end if;
end;
end;
end;
return difference
end calcTargetTotal;
这将按预期在单元测试中多线程永不失败。
但是我们已经看到了正在运行的系统上加载时,这个失败,堆栈跟踪看起来像这样:
ORA-01403: no data found
ORA-00001: unique constraint() violated
ORA-01403: no data found
行号(我已经删除,因为它们是无意义的断章取义)验证第一个更新由于没有数据而失败,插入由于唯一性而失败,并且第二次更新在没有数据的情况下失败,这是不可能的。
从我在其他线程上读到的MERGE语句也不是原子的,可能会遇到类似的问题。
有没有人有任何想法如何防止这种情况发生?
是否只有一列(accountID)上的唯一索引?还是有没有第二列,你没有显示,以简化说明? – redcayuga 2011-03-09 16:39:15
如何定义唯一约束?一个独特的索引?具有唯一显式约束的非唯一索引?如果定义了明确的唯一约束,它是否可延迟? – redcayuga 2011-03-09 16:47:28