Защо това не работи?
Вярвам, че поведението по подразбиране на SQL Server е да освобождава споделени ключалки веднага щом те вече не са необходими. Вашата подзаявка ще доведе до краткотрайно споделено (S) заключване на таблицата, което ще бъде освободено веднага щом подзаявката завърши.
В този момент нищо не пречи на едновременна транзакция да вмъкне самия ред, който току-що потвърдихте, че не присъства.
Каква промяна трябва да направя, за да няма шанс за изключение поради нарушение на ограничението?
Добавяне на HOLDLOCK
hint към вашата подзаявка ще инструктира SQL Server да задържи заключването, докато транзакцията бъде завършена. (Във вашия случай това е неявна транзакция.) HOLDLOCK
hint е еквивалентен на SERIALIZABLE
намек, който сам по себе си е еквивалентен на нивото на изолация на транзакциите, което може да се сериализира, което споменавате в списъка си с „други подходи“.
HOLDLOCK
Самият намек би бил достатъчен за запазване на S заключването и предотвратяване на едновременна транзакция от вмъкване на реда, от който се пазите. Вероятно обаче ще откриете, че вашата уникална грешка при нарушение на ключа е заменена от блокиране, възникващо със същата честота.
Ако запазвате само S заключване на масата, помислете за надпревара между два едновременни опита за вмъкване на един и същ ред, продължавайки в lockstep - и двата успяват да придобият S заключване на масата, но нито един не може да успее да придобие Exclusive (X) необходимо заключване за изпълнение на вмъкването.
За щастие има друг тип заключване за този точен сценарий, наречен Актуализиране (U) заключване. U заключването е идентично на S заключване със следната разлика:докато множество S ключалки могат да се държат едновременно на един и същ ресурс, само една U заключване може да бъде задържана в даден момент. (Казано по друг начин, докато S ключалките са съвместими помежду си (т.е. могат да съществуват съвместно без конфликт), U ключалките не са съвместими една с друга, но могат да съществуват заедно с S ключалки; и по-нататък по спектъра, изключителните (X) брави не са съвместим с S или U ключалки)
Можете да надстроите имплицитното заключване S на вашата подзаявка до U заключване с помощта на UPDLOCK
намек.
Два едновременни опита за вмъкване на един и същ ред в таблицата вече ще се сериализират в първоначалния оператор за избор, тъй като това придобива (и задържа) U заключване, което не е съвместимо с друго U заключване от едновременния опит за вмъкване.
NULL стойности
Отделен проблем може да възникне от факта, че FieldC позволява NULL стойности.
Ако ANSI_NULLS
е включено (по подразбиране), тогава проверката за равенство FieldC=NULL
ще върне false, дори в случай, когато FieldC е NULL (трябва да използвате IS NULL
оператор за проверка за нула, когато ANSI_NULLS
е включен). Тъй като FieldC е нула, вашата дублирана проверка няма да работи при вмъкване на стойност NULL.
За да се справите правилно с нулевите стойности, ще трябва да модифицирате подзаявката си EXISTS, за да използвате IS NULL
оператор, а не =
когато се вмъква стойност NULL. (Или можете да промените таблицата, за да забраните NULL във всички съответни колони.)
Онлайн справочници за SQL Server Books
- Съвети за заключване
- Матрица за съвместимост на заключване
- ANSI_NULLS