Можете да използвате LOCK, за да направите нещата СЕРИАЛИЗИРУЕМИ, но това намалява едновременността. Защо не опитате първо общото състояние („предимно вмъкване или предимно изберете“), последвано от безопасно обработване на „поправително“ действие? Тоест моделът "JFDI"...
Очакват се предимно INSERT (парк на топката 70-80%+):
Просто опитайте да вмъкнете. Ако не успее, редът вече е създаден. Няма нужда да се притеснявате за едновременността, защото TRY/CATCH се занимава с дубликати вместо вас.
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
Предимно SELECTs:
Подобно, но първо се опитайте да получите данни. Няма данни =необходимо е INSERT. Отново, ако 2 едновременни повиквания се опитат да INSERT, защото и двамата са открили, че в реда липсват манипулаторите TRY/CATCH.
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
Вторият изглежда се повтаря, но е много едновременен. Заключванията биха постигнали същото, но за сметка на едновременността...
Редактиране:
Защоне за да използвате MERGE...
Ако използвате клаузата OUTPUT, тя ще върне само това, което е актуализирано. Така че имате нужда от фиктивна UPDATE, за да генерирате таблицата INSERTED за клаузата OUTPUT. Ако трябва да правите фиктивни актуализации с много обаждания (както се подразбира от OP), това е много дневник записва просто за да можете да използвате MERGE.