Waarom werkt dit niet?
Ik geloof dat het standaardgedrag van SQL Server is om gedeelde vergrendelingen vrij te geven zodra ze niet langer nodig zijn. Uw subquery resulteert in een kortstondige gedeelde (S) lock op de tafel, die wordt vrijgegeven zodra de subquery is voltooid.
Op dit moment is er niets dat een gelijktijdige transactie verhindert om de rij in te voegen waarvan u zojuist heeft geverifieerd dat deze niet aanwezig was.
Welke wijziging moet ik aanbrengen zodat er geen kans is op een uitzondering vanwege de schending van de beperking?
De HOLDLOCK
. toevoegen hint naar uw subquery zal SQL Server instrueren om de vergrendeling vast te houden totdat de transactie is voltooid. (In uw geval is dit een impliciete transactie.) De HOLDLOCK
hint is gelijk aan de SERIALIZABLE
hint, die zelf gelijk is aan het serialiseerbare transactie-isolatieniveau waarnaar u verwijst in uw lijst met "andere benaderingen".
De HOLDLOCK
een hint alleen zou voldoende zijn om de S-lock te behouden en te voorkomen dat een gelijktijdige transactie de rij invoegt waartegen u waakt. U zult echter waarschijnlijk merken dat uw unieke sleutelovertredingsfout wordt vervangen door deadlocks, die zich met dezelfde frequentie voordoen.
Als u alleen een S-lock op de tafel behoudt, overweeg dan een race tussen twee gelijktijdige pogingen om dezelfde rij in te voegen, waarbij u in lockstep verdergaat -- beide slagen erin een S-lock op de tafel te verwerven, maar geen van beide kan erin slagen de Exclusive te verwerven (X) slot vereist om het invoegen uit te voeren.
Gelukkig is er een ander type slot voor dit exacte scenario, het Update (U) slot. Het U-slot is identiek aan een S-slot met het volgende verschil:terwijl meerdere S-sloten tegelijkertijd op dezelfde bron kunnen worden gehouden, kan er slechts één U-slot tegelijk worden vastgehouden. (Anders gezegd, terwijl S-sloten compatibel met elkaar zijn (d.w.z. zonder conflict naast elkaar kunnen bestaan), zijn U-sloten niet compatibel met elkaar, maar kunnen ze naast S-sloten naast elkaar bestaan; en verder in het spectrum zijn exclusieve (X)-sloten niet compatibel met S- of U-sloten)
U kunt de impliciete S-lock op uw subquery upgraden naar een U-lock met behulp van de UPDLOCK
hint.
Twee gelijktijdige pogingen om dezelfde rij in de tabel in te voegen, worden nu geserialiseerd bij de eerste select-instructie, omdat hiermee een U-lock wordt verkregen (en vastgehouden), die niet compatibel is met een ander U-lock van de gelijktijdige invoegpoging.
NULL-waarden
Een apart probleem kan ontstaan door het feit dat FieldC NULL-waarden toestaat.
Als ANSI_NULLS
is ingeschakeld (standaard) dan de gelijkheidscontrole FieldC=NULL
zou false retourneren, zelfs in het geval waar FieldC NULL is (u moet de IS NULL
gebruiken operator om te controleren op null wanneer ANSI_NULLS
staat aan). Aangezien FieldC nullable is, werkt uw dubbele controle niet bij het invoegen van een NULL-waarde.
Om correct met nulls om te gaan, moet u uw EXISTS-subquery aanpassen om de IS NULL
te gebruiken operator in plaats van =
wanneer een waarde van NULL wordt ingevoegd. (Of u kunt de tabel wijzigen om NULL's in alle betrokken kolommen niet toe te staan.)
SQL Server Boeken Online Referenties
- Hints voor vergrendelen
- Compatibiliteitsmatrix vergrendelen
- ANSI_NULLS