Du kan bruge LÅSE til at gøre tingene SERIALISERbare, men dette reducerer samtidighed. Hvorfor ikke prøve den almindelige tilstand først ("for det meste indsæt eller for det meste vælg") efterfulgt af sikker håndtering af "afhjælpende" handling? Det vil sige "JFDI"-mønsteret...
For det meste INSERTs forventes (boldpark 70-80%+):
Prøv bare at indsætte. Hvis det mislykkes, er rækken allerede oprettet. Ingen grund til at bekymre sig om samtidighed, fordi TRY/CATCH håndterer dubletter for dig.
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
Mest SELECTs:
Lignende, men prøv at få data først. Ingen data =INDSÆT nødvendig. Igen, hvis 2 samtidige opkald prøv at INSERT, fordi de begge fandt, at rækken mangler TRY/CATCH-håndtagene.
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
Den anden ser ud til at gentage sig selv, men den er meget samtidig. Låse ville opnå det samme, men på bekostning af samtidighed...
Rediger:
Hvorfor ikke at bruge FLET...
Hvis du bruger OUTPUT-klausulen, vil den kun returnere det, der er opdateret. Så du har brug for en dummy UPDATE for at generere INSERTED-tabellen til OUTPUT-klausulen. Hvis du skal lave dummy-opdateringer med mange opkald (som antydet af OP), er det en masse log, skriver bare for at kunne bruge MERGE.