Hvorfor virker dette ikke?
Jeg tror, at standardadfærden for SQL Server er at frigive delte låse, så snart de ikke længere er nødvendige. Din underforespørgsel vil resultere i en kortvarig delt (S) lås på bordet, som frigives, så snart underforespørgslen er fuldført.
På dette tidspunkt er der intet, der forhindrer en samtidig transaktion i at indsætte den række, du lige har bekræftet, ikke var til stede.
Hvilken ændring skal jeg foretage, så der ikke er nogen chance for en undtagelse på grund af overtrædelsen af begrænsningen?
Tilføjelse af HOLDLOCK
hint til din underforespørgsel vil instruere SQL Server til at holde på låsen, indtil transaktionen er gennemført. (I dit tilfælde er dette en implicit transaktion.) HOLDLOCK
tip svarer til SERIALIZABLE
hint, som i sig selv svarer til det serialiserbare transaktionsisoleringsniveau, som du henviser til i din liste over "andre tilgange".
HOLDLOCK
tip alene ville være tilstrækkeligt til at bevare S-låsen og forhindre en samtidig transaktion i at indsætte den række, du beskytter dig imod. Du vil dog sandsynligvis finde din unikke nøglekrænkelsesfejl erstattet af deadlocks, der forekommer med samme hyppighed.
Hvis du kun beholder en S-lås på bordet, så overvej et kapløb mellem to samtidige forsøg på at indsætte den samme række, fortsæt i låsetrin - begge lykkes med at erhverve en S-lås på bordet, men ingen af dem kan få succes med at erhverve den eksklusive (X) lås påkrævet for at udføre indsættelsen.
Heldigvis er der en anden låsetype til netop dette scenarie, kaldet Update (U) låsen. U-låsen er identisk med en S-lås med følgende forskel:Mens flere S-låse kan holdes samtidigt på den samme ressource, må der kun holdes én U-lås ad gangen. (Sagt på en anden måde, mens S-låse er kompatible med hinanden (dvs. kan eksistere uden konflikt), er U-låse ikke kompatible med hinanden, men kan eksistere side om side med S-låse; og længere hen ad spektret er Exclusive (X) låse ikke kompatibel med enten S- eller U-låse)
Du kan opgradere den implicitte S-lås på din underforespørgsel til en U-lås ved hjælp af UPDLOCK
tip.
To samtidige forsøg på at indsætte den samme række i tabellen vil nu blive serialiseret ved den indledende select-sætning, da denne erhverver (og holder) en U-lås, som ikke er kompatibel med en anden U-lås fra det samtidige indsættelsesforsøg.
NULL-værdier
Et separat problem kan opstå som følge af, at FieldC tillader NULL-værdier.
Hvis ANSI_NULLS
er slået til (standard), så er lighedskontrollen FieldC=NULL
ville returnere falsk, selv i det tilfælde, hvor FieldC er NULL (du skal bruge IS NULL
operatør for at tjekke for null, når ANSI_NULLS
er tændt). Da FieldC er nullbar, vil din duplikatkontrol ikke fungere, når du indsætter en NULL-værdi.
For at håndtere nuller korrekt skal du ændre din EXISTS underforespørgsel til at bruge IS NULL
operator i stedet for =
når en værdi på NULL indsættes. (Eller du kan ændre tabellen til ikke at tillade NULL i alle de pågældende kolonner.)
SQL Server Books Online References
- Låsetip
- Lås kompatibilitetsmatrix
- ANSI_NULLS