У нас была похожая проблема - и вы были бы рады узнать, что вы сможете решить эту проблему, не снимая ограничения FK.
В частности, в нашем сценарии мы часто обновлялиродительская таблица в транзакции READ COMMITTED.У нас также были частые параллельные (длительные) транзакции моментальных снимков, которые требовались для вставки строк в дочернюю таблицу с FK в родительскую таблицу - так что по сути это тот же сценарий, что и у вас, за исключением того, что мы использовали транзакцию READ COMMITTED вместо SEREALIZABLE.
Чтобы решить эту проблему, создайте новое ограничение UNIQUE NONCLUSTERED для первичной таблицы над столбцом FK.Кроме того, вы также должны заново создать FK после того, как создали уникальное ограничение, поскольку это гарантирует, что FK теперь ссылается на ограничение (а не на кластерный ключ).
Примечание. Недостатком является то, что теперь у вас есть внешне избыточное ограничение для таблицы, которое должно поддерживаться сервером SQL при обновлении родительской таблицы.Тем не менее, это может быть хорошей возможностью для вас рассмотреть другой / альтернативный кластеризованный ключ ... и, если вам повезет, это может даже заменить потребность в другом индексе в этой таблице ...
К сожалению, я не могу найти хорошего объяснения в Интернете о том, почему создание уникального ограничения решает проблему.Самый простой способ объяснить, почему это работает, заключается в том, что FK теперь ссылается только на уникальное ограничение, а изменение родительской таблицы (т. Е. Столбцов, не связанных с FK) не вызывает конфликт обновления в транзакции моментального снимка как FK.теперь ссылается на неизменную уникальную запись ограничения.Сравните это с кластеризованным ключом, где изменение любого столбца в родительской таблице повлияет на версию строки в этой таблице - и, поскольку FK видит обновленный номер версии, транзакцию моментального снимка необходимо прервать.
Кроме того, еслиродительская строка удаляется в транзакции без снимка, затем будут затронуты как кластерные, так и уникальные ограничения, и, как и ожидалось, транзакция моментального снимка будет откатываться (так что целостность FK сохраняется).
У меня естьудалось воспроизвести эту проблему, используя приведенный выше пример кода, который я адаптировал из этой записи в блоге
---------------------- SETUP Test database
-- Creating Customers table without unique constraint
USE master;
go
IF EXISTS (SELECT * FROM sys.databases WHERE name = 'SnapshotTest')
BEGIN;
DROP DATABASE SnapshotTest;
END;
go
CREATE DATABASE SnapshotTest;
go
ALTER DATABASE SnapshotTest
SET ALLOW_SNAPSHOT_ISOLATION ON;
go
USE SnapshotTest;
go
CREATE TABLE Customers
(CustID int NOT NULL PRIMARY KEY,CustName varchar(40) NOT NULL);
CREATE TABLE Orders
(OrderID char(7) NOT NULL PRIMARY KEY,
OrderType char(1) CHECK (OrderType IN ('A', 'B')),
CustID int NOT NULL REFERENCES Customers (CustID)
);
INSERT INTO Customers (CustID, CustName) VALUES (1, 'First test customer');
INSERT INTO Customers (CustID, CustName) VALUES (2, 'Second test customer');
GO
---------------------- TEST 1: Run this test before test 2
USE SnapshotTest;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN TRANSACTION;
-- Check to see that the customer has no orders
SELECT * FROM Orders WHERE CustID = 1;
-- Update the customer
UPDATE Customers SET CustName='Updated customer' WHERE CustID = 1;
-- Twiddle thumbs for 10 seconds before commiting
WAITFOR DELAY '0:00:10';
COMMIT TRANSACTION;
go
-- Check results
SELECT * FROM Customers (NOLOCK);
SELECT * FROM Orders (NOLOCK);
GO
---------------------- TEST 2: Run this test in a new session shortly after test 1
USE SnapshotTest;
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
BEGIN TRANSACTION;
SELECT * FROM Customers WHERE CustID = 1;
INSERT INTO Orders (OrderID, OrderType, CustID) VALUES ('Order01', 'A', 1);
-- Twiddle thumbs for 10 seconds before commiting
WAITFOR DELAY '0:00:10';
COMMIT TRANSACTION;
go
-- Check results
SELECT * FROM Customers (NOLOCK);
SELECT * FROM Orders (NOLOCK);
go
И чтобы исправить вышеприведенный сценарий, переустановите тестовую базу данных.Затем запустите следующий скрипт перед запуском Тестов 1 и 2.
ALTER TABLE Customers
ADD CONSTRAINT UX_CustID_ForSnapshotFkUpdates UNIQUE NONCLUSTERED (CustID)
-- re-create the existing FK so it now references the constraint instead of clustered index (the existing FK probably has a different name in your DB)
ALTER TABLE [dbo].[Orders] DROP CONSTRAINT [FK__Orders__CustID__1367E606]
ALTER TABLE [dbo].[Orders] WITH CHECK ADD FOREIGN KEY([CustID])
REFERENCES [dbo].[Customers] ([CustID])
GO