Du kan använda LÅS för att göra saker SERIALISERbara men detta minskar samtidighet. Varför inte prova det vanliga tillståndet först ("för det mesta infoga eller mestadels välj") följt av säker hantering av "avhjälpande" åtgärder? Det vill säga "JFDI"-mönstret...
Mestadels INSERTs förväntas (bollpark 70-80%+):
Försök bara lägga in. Om det misslyckas har raden redan skapats. Du behöver inte oroa dig för samtidighet eftersom TRY/CATCH hanterar dubbletter åt 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
Mestadels SELECT:
Liknande, men försök att få data först. Inga data =INFO behövs. Återigen, om två samtidiga anrop försöker INFOGA eftersom de båda hittade att raden saknade TRY/CATCH-handtagen.
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 andra verkar upprepa sig, men den är mycket samtidig. Lås skulle uppnå samma sak men på bekostnad av samtidighet...
Redigera:
Varför inte för att använda MERGE...
Om du använder OUTPUT-satsen kommer den bara att returnera det som är uppdaterat. Så du behöver en dummy UPDATE för att generera INSERTED-tabellen för OUTPUT-satsen. Om du måste göra dummy-uppdateringar med många anrop (som antyds av OP) är det mycket logg som skriver bara för att kunna använda MERGE.