S MERGE jako takovým to není problém. Problém spočívá spíše ve vaší aplikaci. Zvažte tuto uloženou proceduru:
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;
Toto je PL/SQL ekvivalent MERGE na T23. Co se stane, když to zavolají dvě relace současně?
SSN1> exec upsert_t23(100, 'FOX IN SOCKS')
SSN2> exec upsert_t23(100, 'MR KNOX')
SSN1 se tam dostane jako první, nenajde žádný odpovídající záznam a vloží záznam. SSN2 se tam dostane jako druhý, ale před potvrzením SSN1 nenajde žádný záznam, vloží záznam a zablokuje se protože SSN1 má zámek na jedinečném indexovém uzlu na 100. Když SSN1 potvrdí SSN2, vrhne narušení DUP_VAL_ON_INDEX.
Příkaz MERGE funguje úplně stejně. Obě relace zkontrolují on (t23.id = 100)
, nenajděte ji a přejděte dolů po větvi INSERT. První relace bude úspěšná a druhá vrhne ORA-00001.
Jedním ze způsobů, jak to zvládnout, je zavést pesimistické zamykání. Na začátku procedury UPSERT_T23 uzamkneme tabulku:
...
lock table t23 in row shared mode nowait;
open c;
...
Nyní dorazí SSN1, uchopí zámek a pokračuje jako předtím. Když SSN2 dorazí, nemůže získat zámek, takže okamžitě selže. Což je pro druhého uživatele frustrující, ale alespoň nevisí a navíc vědí, že na stejném záznamu pracuje někdo jiný.
Neexistuje žádná syntaxe pro INSERT, která je ekvivalentní SELECT ... FOR UPDATE, protože není co vybírat. A tak žádná taková syntaxe neexistuje ani pro MERGE. Co musíte udělat, je zahrnout příkaz LOCK TABLE do programové jednotky, která vydá MERGE. Zda je to pro vás možné, závisí na frameworku, který používáte.