sql >> Databasteknik >  >> RDS >> Oracle

Kan jag göra en atomic MERGE i Oracle?

Detta är inte ett problem med MERGE som sådan. Snarare ligger problemet i din ansökan. Tänk på denna lagrade procedur:

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;

Så detta är PL/SQL-motsvarigheten till en MERGE på T23. Vad händer om två sessioner anropar det samtidigt?

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

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

SSN1 kommer dit först, hittar ingen matchande post och infogar en post. SSN2 kommer dit tvåa men innan SSN1 commit, hittar ingen post, infogar en post och hänger sig eftersom SSN1 har ett lås på den unika indexnoden för 100. När SSN1 begår kommer SSN2 att utlösa en DUP_VAL_ON_INDEX-överträdelse.

MERGE-satsen fungerar på exakt samma sätt. Båda sessionerna kommer att kontrollera on (t23.id = 100) , inte hitta den och gå ner för INSERT-grenen. Den första sessionen kommer att lyckas och den andra kommer att kasta ORA-00001.

Ett sätt att hantera detta är att införa pessimistisk låsning. I början av UPSERT_T23-proceduren låser vi tabellen:

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

Nu anländer SSN1, tar tag i låset och fortsätter som tidigare. När SSN2 anländer kan den inte få låset, så det misslyckas omedelbart. Vilket är frustrerande för den andra användaren, men de hänger sig åtminstone inte, plus att de vet att någon annan arbetar med samma skiva.

Det finns ingen syntax för INSERT som motsvarar SELECT ... FOR UPDATE, eftersom det inte finns något att välja. Och så det finns ingen sådan syntax för MERGE heller. Vad du behöver göra är att inkludera LOCK TABLE-satsen i den programenhet som utfärdar MERGE. Om detta är möjligt för dig beror på vilket ramverk du använder.



  1. Ta bort tabeller med mysqli_multi_query()

  2. Konvertera tabell från MyISAM till INNODB

  3. mysql trunkerar heltal till ett konstigt tal vid hitta och infoga

  4. Fel vid användning av en OLAP-anslutning:MSOLAP-leverantören är inte registrerad på den lokala datorn...