Můžete použít ZÁMKY, aby se věci staly SERIALIZAČNÍMI, ale to snižuje souběžnost. Proč nezkusit nejprve běžnou podmínku („většinou vložit nebo většinou vybrat“) a poté bezpečně provést „nápravnou“ akci? Tedy vzor "JFDI"...
Očekávané většinou INSERTy (ball park 70-80 %+):
Jen zkuste vložit. Pokud selže, řádek již byl vytvořen. Není třeba se obávat souběžnosti, protože TRY/CATCH řeší duplikáty za vás.
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
Většinou SELECT:
Podobné, ale zkuste nejprve získat data. Žádná data =potřeba INSERT. Znovu, pokud se 2 souběžná volání pokusí o INSERT, protože oba nalezli v řádku chybějící úchyty 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
Zdá se, že druhý se opakuje, ale je vysoce souběžný. Zámky by dosáhly stejného, ale na úkor souběžnosti...
Upravit:
Proč ne použít MERGE...
Pokud použijete klauzuli OUTPUT, vrátí pouze to, co je aktualizováno. K vygenerování tabulky INSERTED pro klauzuli OUTPUT tedy potřebujete fiktivní UPDATE. Pokud musíte provádět fiktivní aktualizace s mnoha hovory (jak předpokládá OP), znamená to, že se do protokolu zapisuje mnoho jen abyste mohli používat MERGE.