Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Posso fazer um MERGE atômico no Oracle?


Este não é um problema com MERGE como tal. Em vez disso, o problema está no seu aplicativo. Considere este procedimento armazenado:
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;

Então, este é o equivalente PL/SQL de um MERGE em T23. O que acontece se duas sessões o chamarem simultaneamente?
SSN1>  exec upsert_t23(100, 'FOX IN SOCKS')

SSN2>  exec upsert_t23(100, 'MR KNOX')

O SSN1 chega primeiro, não encontra nenhum registro correspondente e insere um registro. O SSN2 chega em segundo lugar, mas antes do commit do SSN1, não encontra nenhum registro, insere um registro e trava porque SSN1 tem um bloqueio no nó de índice exclusivo para 100. Quando SSN1 confirma, SSN2 lançará uma violação DUP_VAL_ON_INDEX.

A instrução MERGE funciona exatamente da mesma maneira. Ambas as sessões verificarão on (t23.id = 100) , não o encontre e desça a ramificação INSERT. A primeira sessão será bem-sucedida e a segunda lançará ORA-00001.

Uma maneira de lidar com isso é introduzir o bloqueio pessimista. No início do procedimento UPSERT_T23 bloqueamos a tabela:
...
lock table t23 in row shared mode nowait;
open c;
...

Agora, SSN1 chega, pega a fechadura e continua como antes. Quando o SSN2 chega, ele não consegue o bloqueio, então ele falha imediatamente. O que é frustrante para o segundo usuário, mas pelo menos eles não estão pendurados, além de saberem que outra pessoa está trabalhando no mesmo registro.

Não há sintaxe para INSERT que seja equivalente a SELECT ... FOR UPDATE, porque não há nada para selecionar. E, portanto, também não existe essa sintaxe para MERGE. O que você precisa fazer é incluir a instrução LOCK TABLE na unidade do programa que emite o MERGE. Se isso é possível para você depende da estrutura que você está usando.