Транзакция изоляции моментального снимка прервана из-за конфликта обновления

Следующее положение:

INSERT INTO dbo.Changes([Content], [Date], [UserId], [CompanyId]) 
  VALUES (@1, @2, @3, @4);
SELECT @@identity;

дает мне эту ошибку SQL 3960:

Транзакция изоляции моментального снимка прервана из-за конфликта обновления. Вы не можете использовать изоляцию моментальных снимков для прямого или косвенного доступа к таблице 'dbo.Companies' в базе данных 'myDatabase', чтобы обновить, удалить или вставить строку, которая была изменена или удалена другой транзакцией. Повторите транзакцию или измените уровень изоляции для оператора update / delete.

Насколько я понял, из сообщения об ошибке не следует обновлять, удалять или вставлять в таблицу dbo.Companies во время модификации другого соединения dbo.Companies,

Но почему это происходит, когда я вставляю новую строку в другую таблицу dbo.Changes (который имеет внешний ключ к dbo.Companies) и я не удалял указанную строку в dbo.Companies, но я просто обновлял строку в dbo.Companies а не первичный ключ? Это должно работать нормально, не так ли? (Это ошибка в SQL Server?)

ОБНОВИТЬ:

Таблицы выглядит следующим образом:

dbo.Changes([Id] int PK, [Content] nvarchar, 
  [Date] datetime, [UserId] int, [CompanyId] int -> dbo.Companies.[Id])
dbo.Companies([Id] int PK, [Name] nvarchar)

Второе обновление делает:

UPDATE dbo.Companies WHERE [Id] = @1 SET [Name] = @2;

2 ответа

Решение

Похоже, что SQL Server будет получать блокировки обновления для любой записи, которую он должен прочитать, даже если он не изменяет ее.

Дополнительная информация по этой теме microsoft.public.sqlserver.server:

Без вспомогательного индекса на CustomerContactPerson, заявление

УДАЛИТЬ ОТ Контактного лица WHERE ID = @ID;

Потребуется "текущее" чтение всех строк в CustomerContactPerson, чтобы убедиться, что нет строк CustomerContactPerson, которые ссылаются на удаленную строку ContactPerson. С помощью индекса DELETE может определить, что в CustomerContactPerson нет связанных строк, не считав строки, затронутые другой транзакцией.

Кроме того, в транзакции моментального снимка шаблон для чтения данных, которые вы собираетесь развернуть и обновить, должен выполнить UPDLOCK при чтении. Это гарантирует, что вы производите обновление на основе "текущих" данных, а не "непротиворечивых" (моментальных снимков) данных, и что при выпуске DML данные не будут заблокированы, и вы не будете невольно перезаписывать изменение другой сессии.

Для нас исправлением было добавление индексов к внешним ключам.

В вашем примере я подозреваю, что добавление индекса в Changes.CompanyId поможет. Я не уверен, что это реальное решение. Может ли оптимизатор SQL Server отказаться от использования индекса?

SQL Server может видеть обновление зависимой таблицы, которое МОЖЕТ изменить поведение вставки... мне кажется справедливым, поскольку SQL не может угадать, какая другая логика может зависеть от столбца [name] (триггеры и т. Д.)

если ваши приложения реализуют логику повторных попыток взаимоблокировки, вы можете изменить их так, чтобы они обрабатывали ошибку № 3960 так же, как ошибку № 1205, и автоматически повторяли...

Другие вопросы по тегам