Другое распространенное объяснение этого может быть связано с индексами UNIQUE с включенной опцией IGNORE_DUP_KEY.
From BOL - этот параметр указывает реакцию на ошибку, когда операция вставки пытается вставить повторяющиеся значения ключа в уникальный индекс. Параметр IGNORE_DUP_KEY применяется только для операций вставки после создания или перестроения индекса. Опция не действует при выполнении CREATE INDEX, ALTER INDEX или UPDATE. По умолчанию установлено значение OFF.
ON
Предупреждающее сообщение появится, когда дублированные значения ключа будут вставлены в уникальный индекс. Только строки, нарушающие ограничение уникальности, потерпят неудачу.
OFF
Сообщение об ошибке будет появляться при вставке дублированных значений ключа в уникальный индекс. Вся операция INSERT будет отменена.
Что они здесь не упоминают, так это то, что SERIALIZABLE изоляция применяется во время INSERTS, когда эта опция включена. Лично я не понял внутреннее требование сделать это, поскольку некоторые вставленные строки могут быть отброшены и ничего больше, но это то, что есть. Поставьте команду разработчиков SQL в очередь ...
Это поведение легко демонстрируется;
Сначала создайте новую таблицу с типичным PK:
CREATE TABLE [dbo].[Test_RC_TIL_RangeLocks](
[RID] [int] IDENTITY(1,1) NOT NULL,
[Col1] [int] NOT NULL,
[Col2] [int] NOT NULL,
[Col3] [int] NOT NULL
) ON [PRIMARY]
Далее мы хотим добавить индекс UNIQUE для Col1 и Col2 с IGNORE_DUP_KEY ON:
CREATE UNIQUE NONCLUSTERED INDEX [UIX_Test_RC_TIL_RangeLocks] ON [dbo].[Test_RC_TIL_RangeLocks](
[Col1] ASC,
[Col2] ASC
)WITH (
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF,
IGNORE_DUP_KEY = ON, --<<**THE OFFENDER**>>
DROP_EXISTING = OFF,
ONLINE = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY]
Далее мы добавим 5 строк и посмотрим, что произойдет ...
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
DECLARE @C int
SELECT @C=5
BEGIN TRANSACTION
WHILE @C>0
BEGIN
INSERT Test_RC_TIL_RangeLocks(Col1,Col2,Col3)
VALUES (@C,@C+1,2*@C + 100)
SET @C=@C-1
END
SELECT * FROM Test_RC_TIL_RangeLocks
EXEC sp_lock @@SPID
COMMIT
Как и ожидалось, мы добавили пять строк:
RID Col1 Col2 Col3
1 5 6 110
2 4 5 108
3 3 4 106
4 2 3 104
5 1 2 102
И замки, которые нас интересуют:
sid ObjId IndId Type Resource Mode
53 0 0 DB S
53 0 0 DB S
53 402100473 1 KEY (8194443284a0) X
53 402100473 2 KEY (550e0a2a4b96) RangeX-X
53 402100473 2 KEY (ffffffffffff) RangeS-U
53 1131151075 0 TAB IS
53 402100473 1 PAG 1:744 IX
53 402100473 2 PAG 1:748 IX
53 402100473 1 KEY (98ec012aa510) X
53 402100473 1 KEY (a0c936a3c965) X
53 402100473 2 KEY (ec04ac4bee1f) RangeX-X
53 402100473 0 TAB IX
53 402100473 2 KEY (0207a0a08e23) RangeX-X
53 402100473 2 KEY (7112ec63c430) RangeX-X
53 402100473 1 KEY (59855d342c69) X
53 402100473 1 KEY (61a06abd401c) X
53 338100245 0 TAB IX
Ах, страшные СЕРИАЛИЗИРУЕМЫЕ блокировки диапазона ключей, вызываемые с уровнем изоляции READ COMMITTED на простой вставке!
Так что, когда BOL говорит;
Перед блокировкой диапазона клавиш должны быть выполнены следующие условия:
• Уровень изоляции транзакции должен быть установлен в SERIALIZABLE.
Помните, что это не всегда так ...
p.s. Как заметка разработчика, слепое удаление дублирующих ключей, которые могут иметь уникальные значения строк, обычно является плохой практикой. Лучше всего убедиться, что вы не пытаетесь вставить дубликаты ключей как часть вашего оператора INSERT ...
Приветствия ...