sql >> Base de Datos >  >> RDS >> Oracle

¿Puedo hacer una MERGE atómica en Oracle?

Esto no es un problema con MERGE como tal. Más bien, el problema radica en su aplicación. Considere este procedimiento almacenado:

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;

Entonces, este es el equivalente PL/SQL de MERGE en T23. ¿Qué sucede si dos sesiones lo llaman simultáneamente?

SSN1>  exec upsert_t23(100, 'FOX IN SOCKS')

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

SSN1 llega primero, no encuentra ningún registro coincidente e inserta un registro. SSN2 llega en segundo lugar, pero antes de que SSN1 se confirme, no encuentra ningún registro, inserta un registro y cuelga porque SSN1 tiene un bloqueo en el nodo de índice único para 100. Cuando SSN1 confirma, SSN2 arrojará una violación DUP_VAL_ON_INDEX.

La declaración MERGE funciona exactamente de la misma manera. Ambas sesiones comprobarán on (t23.id = 100) , no lo busques y baja a la rama INSERT. La primera sesión tendrá éxito y la segunda lanzará ORA-00001.

Una forma de manejar esto es introducir un bloqueo pesimista. Al inicio del procedimiento UPSERT_T23 bloqueamos la tabla:

...
lock table t23 in row shared mode nowait;
open c;
...

Ahora, llega SSN1, agarra el candado y procede como antes. Cuando llega SSN2, no puede obtener el bloqueo, por lo que falla de inmediato. Lo cual es frustrante para el segundo usuario, pero al menos no se cuelga, además sabe que otra persona está trabajando en el mismo registro.

No hay una sintaxis para INSERTAR que sea equivalente a SELECCIONAR... PARA ACTUALIZAR, porque no hay nada que seleccionar. Y, por lo tanto, tampoco existe tal sintaxis para MERGE. Lo que debe hacer es incluir la instrucción LOCK TABLE en la unidad de programa que emite MERGE. Si esto es posible para usted depende del marco que esté utilizando.