これは、MERGE自体の問題ではありません。むしろ、問題はアプリケーションにあります。このストアドプロシージャを検討してください:
create or replace procedure upsert_t23
( p_id in t23.id%type
, p_name in t23.name%type )
is
cursor c is
select null
from t23
where id = p_id;
dummy varchar2(1);
begin
open c;
fetch c into dummy;
if c%notfound then
insert into t23
values (p_id, p_name);
else
update t23
set name = p_name
where id = p_id;
end if;
end;
したがって、これはT23のMERGEに相当するPL/SQLです。 2つのセッションで同時に呼び出すとどうなりますか?
SSN1> exec upsert_t23(100, 'FOX IN SOCKS')
SSN2> exec upsert_t23(100, 'MR KNOX')
SSN1が最初にそこに到達し、一致するレコードが見つからないため、レコードを挿入します。 SSN2は2番目に到達しますが、SSN1がコミットする前に、レコードが見つからず、レコードを挿入してハングします SSN1は100の一意のインデックスノードをロックしているためです。SSN1がコミットすると、SSN2はDUP_VAL_ON_INDEX違反をスローします。
MERGEステートメントはまったく同じように機能します。どちらのセッションもon (t23.id = 100)
をチェックします 、それを見つけられず、INSERTブランチを下に移動します。最初のセッションは成功し、2番目のセッションはORA-00001を投げます。
これを処理する1つの方法は、悲観的なロックを導入することです。 UPSERT_T23プロシージャの開始時に、テーブルをロックします。
...
lock table t23 in row shared mode nowait;
open c;
...
これで、SSN1が到着し、ロックを取得して、前と同じように進みます。 SSN2が到着すると、ロックを取得できないため、すぐに失敗します。これは2人目のユーザーにとっては苛立たしいことですが、少なくとも彼らはぶら下がっていません。さらに、他の誰かが同じレコードで作業していることを知っています。
選択するものがないため、SELECT ...FORUPDATEと同等のINSERTの構文はありません。したがって、MERGEにもそのような構文はありません。あなたがする必要があるのは、MERGEを発行するプログラムユニットにLOCKTABLEステートメントを含めることです。これが可能かどうかは、使用しているフレームワークによって異なります。