Вы можете использовать LOCKS, чтобы сделать вещи SERIALIZABLE, но это уменьшает параллелизм. Почему бы не попробовать сначала общее условие («в основном вставить или в основном выбрать»), а затем выполнить безопасную обработку «корректирующего» действия? То есть шаблон "JFDI" ...
Ожидаются в основном INSERT (парк мячей 70-80% +):
Просто попробуйте вставить. Если это не удается, строка уже создана. Не нужно беспокоиться о параллелизме, потому что TRY / CATCH имеет дело с дубликатами для вас.
BEGIN TRY
INSERT Table VALUES (@Value)
SELECT @id = SCOPEIDENTITY()
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
В основном ВЫБИРАЕТ:
Аналогично, но сначала попробуйте получить данные. Нет данных = ВСТАВКА нужна. Опять же, если 2 одновременных вызова пытаются ВСТАВИТЬ, потому что они обнаружили, что в строке отсутствуют маркеры TRY / CATCH.
BEGIN TRY
SELECT @id = RowID FROM Table WHERE RowValue = @VALUE
IF @@ROWCOUNT = 0
BEGIN
INSERT Table VALUES (@Value)
SELECT @id = SCOPEIDENTITY()
END
END TRY
BEGIN CATCH
IF ERROR_NUMBER() <> 2627
RAISERROR etc
ELSE
SELECT @id = RowID FROM Table WHERE RowValue = @VALUE
END CATCH
2-й, кажется, повторяется, но он очень параллельный. Замки достигли бы того же, но за счет параллелизма ...
Edit:
Почему не для использования MERGE ...
Если вы используете предложение OUTPUT, оно вернет только то, что обновлено. Таким образом, вам нужно фиктивное ОБНОВЛЕНИЕ для генерации таблицы INSERTED для предложения OUTPUT. Если вам приходится делать фиктивные обновления со многими вызовами (как подразумевается в OP), это означает, что в журнал заносится всего , чтобы можно было использовать MERGE.