Ini bukan masalah dengan MERGE. Sebaliknya masalahnya terletak pada aplikasi Anda. Pertimbangkan prosedur tersimpan ini:
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;
Jadi, ini adalah PL/SQL yang setara dengan MERGE di T23. Apa yang terjadi jika dua sesi memanggilnya secara bersamaan?
SSN1> exec upsert_t23(100, 'FOX IN SOCKS')
SSN2> exec upsert_t23(100, 'MR KNOX')
SSN1 sampai di sana terlebih dahulu, tidak menemukan catatan yang cocok dan memasukkan catatan. SSN2 sampai di sana kedua tetapi sebelum SSN1 melakukan, tidak menemukan catatan, memasukkan catatan dan hang karena SSN1 memiliki kunci pada node indeks unik untuk 100. Ketika SSN1 melakukan SSN2 akan melemparkan pelanggaran DUP_VAL_ON_INDEX.
Pernyataan MERGE bekerja dengan cara yang persis sama. Kedua sesi akan memeriksa on (t23.id = 100)
, tidak menemukannya dan turun ke cabang INSERT. Sesi pertama akan berhasil dan sesi kedua akan melemparkan ORA-00001.
Salah satu cara untuk menangani ini adalah dengan menerapkan penguncian pesimistis. Pada awal prosedur UPSERT_T23 kami mengunci tabel:
...
lock table t23 in row shared mode nowait;
open c;
...
Sekarang, SSN1 tiba, mengambil kunci dan melanjutkan seperti sebelumnya. Ketika SSN2 tiba tidak bisa mendapatkan kunci, jadi langsung gagal. Yang membuat frustasi bagi pengguna kedua tetapi setidaknya mereka tidak hang, ditambah lagi mereka tahu orang lain sedang mengerjakan rekaman yang sama.
Tidak ada sintaks untuk INSERT yang setara dengan SELECT ... FOR UPDATE, karena tidak ada yang bisa dipilih. Jadi tidak ada sintaks seperti itu untuk MERGE juga. Yang perlu Anda lakukan adalah memasukkan pernyataan LOCK TABLE di unit program yang mengeluarkan MERGE. Apakah ini mungkin untuk Anda tergantung pada kerangka kerja yang Anda gunakan.