Oracle
 sql >> Base de données >  >> RDS >> Oracle

Puis-je faire une fusion atomique dans Oracle ?

Ce n'est pas un problème avec MERGE en tant que tel. Le problème réside plutôt dans votre application. Considérez cette procédure stockée :

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;

C'est donc l'équivalent PL/SQL d'un MERGE sur T23. Que se passe-t-il si deux sessions l'appellent simultanément ?

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

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

SSN1 y arrive en premier, ne trouve aucun enregistrement correspondant et insère un enregistrement. SSN2 arrive en second mais avant que SSN1 ne s'engage, ne trouve aucun enregistrement, insère un enregistrement et se bloque car SSN1 a un verrou sur le nœud d'index unique pour 100. Lorsque SSN1 s'engage, SSN2 lancera une violation DUP_VAL_ON_INDEX.

L'instruction MERGE fonctionne exactement de la même manière. Les deux sessions vérifieront on (t23.id = 100) , ne le trouvez pas et descendez dans la branche INSERT. La première session réussira et la seconde lancera ORA-00001.

Une façon de gérer cela consiste à introduire un verrouillage pessimiste. Au démarrage de la procédure UPSERT_T23 on verrouille la table :

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

Maintenant, SSN1 arrive, attrape le verrou et procède comme avant. Lorsque SSN2 arrive, il ne peut pas obtenir le verrou, il échoue donc immédiatement. Ce qui est frustrant pour le deuxième utilisateur, mais au moins il n'est pas suspendu, et en plus, il sait que quelqu'un d'autre travaille sur le même enregistrement.

Il n'y a pas de syntaxe pour INSERT qui équivaut à SELECT ... FOR UPDATE, car il n'y a rien à sélectionner. Et donc il n'y a pas non plus une telle syntaxe pour MERGE. Ce que vous devez faire est d'inclure l'instruction LOCK TABLE dans l'unité de programme qui émet le MERGE. Que cela soit possible pour vous dépend du framework que vous utilisez.