Einfügen, wenn nicht vorhanden, gleichzeitig

13

Ich habe Parallelitätsprobleme mit meinen Einfügungen in einer gespeicherten Prozedur. Der relevante Teil des Verfahrens ist:

select @_id = Id from table1 where othervalue = @_othervalue
IF( @_id IS NULL)
BEGIN
    insert into table1 (othervalue) values (@_othervalue)
    select @_id = Id from table1 where othervalue = @_othervalue
END

Wenn wir 3 oder 4 dieser gespeicherten Prozesse gleichzeitig ausführen, erhalten wir gelegentlich mehrere Einfügungen.

Ich plane dies wie folgt zu beheben:

insert into table1 (othervalue) 
    select TOP(1) @_othervalue as othervalue from table1 WITH(UPDLOCK) 
    where NOT EXISTS ( select * from table1 where othervalue = @_othervalue )

select @_id = Id from table1 where othervalue = @_othervalue

Die Frage ist, ist das, wie man gleichzeitig ohne Duplikate in SQL Server einfügt? Die Tatsache, dass ich TOP nur zum einmaligen Einfügen verwenden muss, stört mich.

Chris
quelle
1
Sie müssen TOP nicht verwenden. Entfernen Sie den FROM-Tabellenverweis aus der SELECT-Anweisung.
ErikE
@GSerg Ich denke du hast recht.
Chris

Antworten:

7

Sie können eine merge-Anweisung mit einem serializableHinweis verwenden.

merge table1 with (serializable) as T 
using (select @_othervalue as othervalue) as S
on T.othervalue = S.othervalue
when not matched then
  insert (othervalue) values (othervalue);
Mikael Eriksson
quelle
Haben Sie Ihren Ansatz aus zwei oder mehr Verbindungen gestresst?
AK
2
@AlexKuznetsov - Ich habe es vor einiger Zeit für eine andere Frage zu SO gemacht. Ich habe zwei Registerkarten in SSMS verwendet. Zuerst wurde das insert ... where not exist ...Muster getestet und festgestellt, dass Deadlocks und Schlüsselverletzungen auftreten können. Daher war es erforderlich, Updlock und serialisierbar zu verwenden. Ich habe dann die Merge-Anweisung getestet und dachte, sie würde die Dinge ein bisschen besser handhaben, und zwar, weil es keine Deadlocks gab, aber ich musste trotzdem serializable verwenden, um keine Schlüsselverletzungen zu haben.
Mikael Eriksson
1
Das ist eine wirklich großartige Antwort.
Chris Marisic
5

Wenn Sie keine Duplikate in der Spalte "othervalue" möchten, können Sie dies tun, indem Sie unique constraintin dieser Spalte eine erstellen . Die Abfrage wäre:

 ALTER TABLE table1
 ADD CONSTRAINT unique_c_othervalue UNIQUE(othervalue)

Dies würde einen Fehler auslösen, wenn eine Abfrage versucht, einen doppelten Wert in die Spalte "othervalue" einzufügen.

StanleyJohns
quelle
Wie würde das funktionieren, wenn die eindeutige Einschränkung ein zweizeiliges Tupel ist?
Chris
1
@ Chris Wie haben Sie eine eindeutige Einschränkung, die sich über Zeilen erstreckt?
Aaron Bertrand
@ Aaron Ich habe wahrscheinlich meine Terminologie deaktiviert, aber wir haben zwei Zeilen, die zusammen eindeutig sein müssen. Ich glaube nicht, dass es in unserem Schema durchgesetzt wird.
Chris
2

Verwenden Sie eine eindeutige Einschränkung, wie von @StanleyJohns empfohlen. Verwenden Sie dann BEGIN TRY END TRY um Ihre Einfügeanweisung.

select @_id = Id from table1 where othervalue = @_othervalue
IF( @_id IS NULL)
BEGIN
    BEGIN TRY
        insert into table1 (othervalue) values (@_othervalue)
        select @_id = Id from table1 where othervalue = @_othervalue        
    END TRY
    BEGIN CATCH
        select @_id = Id from table1 where othervalue = @_othervalue        
    END CATCH
END
mrdenny
quelle