Sie können LOCKs verwenden, um Dinge SERIALISIERBAR zu machen, aber dies reduziert die Parallelität. Warum nicht zuerst die allgemeine Bedingung ausprobieren ("meistens einfügen oder meistens auswählen"), gefolgt von einer sicheren Handhabung der "Abhilfe"-Maßnahmen? Das heißt, das "JFDI"-Muster...
Größtenteils INSERTs erwartet (normalerweise 70–80 %+):
Versuchen Sie einfach einzufügen. Wenn dies fehlschlägt, wurde die Zeile bereits erstellt. Sie müssen sich keine Gedanken über Parallelität machen, da TRY/CATCH Duplikate für Sie behandelt.
BEGIN TRY
INSERT Table VALUES (@Value)
SELECT @id = SCOPE_IDENTITY()
END TRY
BEGIN CATCH
IF ERROR_NUMBER() <> 2627
RAISERROR etc
ELSE -- only error was a dupe insert so must already have a row to select
SELECT @id = RowID FROM Table WHERE RowValue = @VALUE
END CATCH
Meistens SELECTs:
Ähnlich, aber versuchen Sie zuerst, Daten zu erhalten. Keine Daten =INSERT erforderlich. Nochmals, wenn 2 gleichzeitige Aufrufe INSERT versuchen, weil sie beide festgestellt haben, dass in der Zeile die TRY/CATCH-Handles fehlen.
BEGIN TRY
SELECT @id = RowID FROM Table WHERE RowValue = @VALUE
IF @@ROWCOUNT = 0
BEGIN
INSERT Table VALUES (@Value)
SELECT @id = SCOPE_IDENTITY()
END
END TRY
BEGIN CATCH
IF ERROR_NUMBER() <> 2627
RAISERROR etc
ELSE
SELECT @id = RowID FROM Table WHERE RowValue = @VALUE
END CATCH
Der zweite scheint sich zu wiederholen, aber er ist sehr gleichzeitig. Sperren würden dasselbe erreichen, aber auf Kosten der Parallelität...
Bearbeiten:
Warum nicht um MERGE zu verwenden...
Wenn Sie die OUTPUT-Klausel verwenden, wird nur das zurückgegeben, was aktualisiert wurde. Sie benötigen also ein Dummy-UPDATE, um die INSERTED-Tabelle für die OUTPUT-Klausel zu generieren. Wenn Sie bei vielen Aufrufen Dummy-Updates durchführen müssen (wie von OP impliziert), ist das nur eine Menge Log-Schreibvorgänge um MERGE verwenden zu können.