我有一個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