Możesz użyć LOCKS, aby rzeczy były SERIALIZOWANE, ale zmniejsza to współbieżność. Dlaczego nie wypróbować najpierw wspólnego warunku („głównie wstawiaj lub w większości wybierz”), a następnie bezpiecznej obsługi akcji „naprawczej”? To znaczy wzorzec „JFDI”...
Oczekiwane głównie INSERT (park piłki 70-80%+):
Po prostu spróbuj wstawić. Jeśli się nie powiedzie, wiersz został już utworzony. Nie musisz się martwić o współbieżność, ponieważ TRY/CATCH zajmuje się duplikatami za Ciebie.
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
Głównie WYBRANE:
Podobnie, ale najpierw spróbuj uzyskać dane. Brak danych =WSTAWKA potrzebna. Ponownie, jeśli 2 równoczesne wywołania spróbują INSERT, ponieważ oba znalazły wiersz bez uchwytów 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
Drugi wydaje się powtarzać, ale jest wysoce zbieżny. Blokady osiągnęłyby to samo, ale kosztem współbieżności...
Edycja:
Dlaczego nie aby użyć MERGE...
Jeśli użyjesz klauzuli OUTPUT, zwróci ona tylko to, co zostało zaktualizowane. Potrzebujesz więc fikcyjnej UPDATE, aby wygenerować tabelę INSERTED dla klauzuli OUTPUT. Jeśli musisz robić fałszywe aktualizacje z wieloma wywołaniami (jak sugeruje OP), to dużo zapisów w dzienniku tylko aby móc korzystać z funkcji MERGE.