Proč to nefunguje?
Věřím, že výchozí chování SQL Serveru je uvolnit sdílené zámky, jakmile již nejsou potřeba. Váš dílčí dotaz bude mít za následek krátkodobý sdílený (S) zámek na stole, který bude uvolněn, jakmile bude dílčí dotaz dokončen.
V tuto chvíli již nic nebrání tomu, aby souběžná transakce vložila právě ten řádek, který jste právě ověřili, že nebyl přítomen.
Jakou úpravu musím provést, aby kvůli porušení omezení nebyla šance na výjimku?
Přidání HOLDLOCK
nápověda k vašemu dílčímu dotazu dá SQL Server pokyn, aby podržel zámek, dokud nebude transakce dokončena. (Ve vašem případě se jedná o implicitní transakci.) HOLDLOCK
nápověda je ekvivalentní k SERIALIZABLE
nápověda, která sama o sobě je ekvivalentem serializovatelné úrovně izolace transakcí, na kterou odkazujete ve svém seznamu „jiných přístupů“.
HOLDLOCK
samotná nápověda by stačila k zachování zámku S a zabránění souběžné transakci ve vložení řádku, před kterým se chráníte. Pravděpodobně však zjistíte, že vaše jedinečná chyba porušení klíče bude nahrazena zablokováním, které se vyskytuje se stejnou frekvencí.
Pokud si na stole ponecháváte pouze zámek S, zvažte závod mezi dvěma souběžnými pokusy o vložení stejného řádku, pokračujte v zamykacím kroku – oběma se podaří získat zámek S na stole, ale ani jednomu se nepodaří získat výhradní (X) zámek potřebný k provedení vložení.
Naštěstí existuje jiný typ zámku pro tento přesný scénář, nazvaný Update (U) zámek. Zámek U je identický se zámkem S s následujícím rozdílem:zatímco na stejném zdroji lze současně držet více zámků S, v jeden okamžik lze držet pouze jeden zámek U. (Řečeno jinak, zatímco zámky S jsou vzájemně kompatibilní (tj. mohou koexistovat bez konfliktu), zámky U nejsou vzájemně kompatibilní, ale mohou koexistovat vedle zámků S; a dále ve spektru nejsou zámky Exclusive (X) kompatibilní se zámky S nebo U)
Implicitní zámek S ve vašem dílčím dotazu můžete upgradovat na zámek U pomocí UPDLOCK
nápověda.
Dva souběžné pokusy o vložení stejného řádku do tabulky budou nyní serializovány v počátečním příkazu select, protože tím získá (a podrží) zámek U, který není kompatibilní s jiným zámkem U ze souběžného pokusu o vložení.
Hodnoty NULL
Samostatný problém může vyvstat ze skutečnosti, že FieldC umožňuje hodnoty NULL.
Pokud ANSI_NULLS
je zapnuto (výchozí), pak kontrola rovnosti FieldC=NULL
vrátí false, a to i v případě, že FieldC je NULL (musíte použít IS NULL
operátor pro kontrolu null při ANSI_NULLS
je zapnuto). Vzhledem k tomu, že pole FieldC má hodnotu null, nebude při vkládání hodnoty NULL vaše kontrola duplicitní fungovat.
Chcete-li správně pracovat s hodnotami null, budete muset upravit svůj poddotaz EXISTS tak, aby používal IS NULL
operátor spíše než =
když se vkládá hodnota NULL. (Nebo můžete změnit tabulku tak, aby ve všech příslušných sloupcích zakazovala hodnoty NULL.)
SQL Server Books Online reference
- Nápovědy k uzamčení
- Zamknout matici kompatibility
- ANSI_NULLS