sql >> Database >  >> RDS >> Oracle

Kan ik een atomaire MERGE doen in Oracle?

Dit is geen probleem met MERGE als zodanig. Het probleem ligt eerder in uw toepassing. Overweeg deze opgeslagen procedure:

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;

Dit is dus het PL/SQL-equivalent van een MERGE op T23. Wat gebeurt er als twee sessies het tegelijkertijd oproepen?

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

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

SSN1 komt daar als eerste aan, vindt geen overeenkomend record en voegt een record in. SSN2 komt daar als tweede, maar voordat SSN1 commit, vindt geen record, voegt een record in en loopt vast omdat SSN1 een slot heeft op het unieke indexknooppunt voor 100. Wanneer SSN1 zich begaat, zal SSN2 een DUP_VAL_ON_INDEX-overtreding veroorzaken.

De MERGE-instructie werkt op precies dezelfde manier. Beide sessies controleren on (t23.id = 100) , vind het niet en ga naar de INSERT-tak. De eerste sessie zal slagen en de tweede zal ORA-00001 slingeren.

Een manier om hiermee om te gaan, is door pessimistische vergrendeling te introduceren. Aan het begin van de UPSERT_T23 procedure vergrendelen we de tafel:

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

Nu arriveert SSN1, grijpt het slot en gaat verder zoals voorheen. Wanneer SSN2 arriveert, kan het het slot niet krijgen, dus het mislukt onmiddellijk. Dat is frustrerend voor de tweede gebruiker, maar ze hangen tenminste niet op, en ze weten ook dat iemand anders aan hetzelfde record werkt.

Er is geen syntaxis voor INSERT die gelijk is aan SELECT ... FOR UPDATE, omdat er niets te selecteren is. En dus is er ook niet zo'n syntaxis voor MERGE. Wat u moet doen is de LOCK TABLE-instructie opnemen in de programma-eenheid die de MERGE uitgeeft. Of dit voor jou mogelijk is, hangt af van het framework dat je gebruikt.



  1. Beperk de waarde van een MySQL-datatype tot een specifiek bereik (bij voorkeur niet ENUM)

  2. hoe query (zoeken) van sql op Amazon Athena die JSON-waarde heeft?

  3. Het aantal rijen gebruiken uit een tijdelijke tabel in een while-lus SQL Server 2008

  4. Versleutelde gegevens opslaan in Postgres