Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Kann ich in Oracle ein atomares MERGE durchführen?

Mit MERGE als solchem ​​ist das kein Problem. Vielmehr liegt das Problem in Ihrer Anwendung. Betrachten Sie diese gespeicherte Prozedur:

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;

Dies ist also das PL/SQL-Äquivalent eines MERGE auf T23. Was passiert, wenn zwei Sitzungen es gleichzeitig aufrufen?

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

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

SSN1 kommt zuerst dort an, findet keinen übereinstimmenden Datensatz und fügt einen Datensatz ein. SSN2 erreicht es als Zweiter, aber bevor SSN1 festschreibt, findet es keinen Datensatz, fügt einen Datensatz ein und hängt da SSN1 den eindeutigen Indexknoten für 100 gesperrt hat. Wenn SSN1 festschreibt, wird SSN2 eine DUP_VAL_ON_INDEX-Verletzung auswerfen.

Die MERGE-Anweisung funktioniert genau so. Beide Sitzungen prüfen on (t23.id = 100) , finden Sie es nicht und gehen Sie den INSERT-Zweig hinunter. Die erste Sitzung wird erfolgreich sein und die zweite wird ORA-00001 schleudern.

Eine Möglichkeit, damit umzugehen, besteht darin, pessimistische Sperren einzuführen. Zu Beginn der UPSERT_T23-Prozedur sperren wir die Tabelle:

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

Nun trifft SSN1 ein, greift nach der Sperre und fährt wie zuvor fort. Wenn SSN2 ankommt, kann es die Sperre nicht bekommen, also schlägt es sofort fehl. Was für den zweiten Benutzer frustrierend ist, aber zumindest hängen sie nicht, und sie wissen, dass jemand anderes an demselben Datensatz arbeitet.

Es gibt keine Syntax für INSERT, die äquivalent zu SELECT ... FOR UPDATE ist, da nichts auszuwählen ist. Daher gibt es auch für MERGE keine solche Syntax. Was Sie tun müssen, ist die Anweisung LOCK TABLE in die Programmeinheit aufzunehmen, die das MERGE ausgibt. Ob dies für Sie möglich ist, hängt von dem Framework ab, das Sie verwenden.