#sql-server #sql-server-2008 #sql-server-2008-r2
#sql-server #sql-server-2008 #sql-server-2008-r2
Вопрос:
Я использую следующие таблицы для реализации подтипов, что является очень распространенным подходом:
CREATE TABLE dbo.Vehicles(
ID INT NOT NULL,
[Type] VARCHAR(5) NOT NULL,
CONSTRAINT Vehicles_PK PRIMARY KEY(ID),
CONSTRAINT Vehicles_UNQ_ID_Type UNIQUE(ID, [Type]),
CONSTRAINT Vehicles_CHK_ValidTypes CHECK([Type] IN ('Car', 'Truck'))
);
GO
CREATE TABLE dbo.Cars(ID INT NOT NULL,
[Type] AS CAST('Car' AS VARCHAR(5)) PERSISTED,
OtherData VARCHAR(10) NULL,
CONSTRAINT Cars_PK PRIMARY KEY(ID),
CONSTRAINT Cars_FK_Vehicles FOREIGN KEY(ID, [Type])
REFERENCES dbo.Vehicles(ID, [Type])
);
GO
-- adding parent rows
INSERT INTO dbo.Vehicles(ID, [Type])
VALUES(1, 'Car'),
(2, 'Truck');
У меня нет проблем с добавлением дочерней строки через INSERT следующим образом:
INSERT INTO dbo.Cars(ID, OtherData)
VALUES(1, 'Some Data');
DELETE FROM dbo.Cars;
Удивительно, но СЛИЯНИЮ не удается добавить одну дочернюю строку:
MERGE dbo.Cars AS TargetTable
USING
( SELECT 1 AS ID ,
'Some Data' AS OtherData
) AS SourceData
ON SourceData.ID = TargetTable.ID
WHEN NOT MATCHED
THEN INSERT (ID, OtherData)
VALUES(SourceData.ID, SourceData.OtherData);
Msg 547, Level 16, State 0, Line 1
The MERGE statement conflicted with the FOREIGN KEY constraint "Cars_FK_Vehicles". The conflict occurred in database "Test", table "dbo.Vehicles".
The statement has been terminated.
Это ошибка при слиянии или я что-то упускаю?
Комментарии:
1. Похоже, что он передает пустую строку как
type
. Если я удалю ограничение проверки и добавлю строкуINSERT INTO dbo.Vehicles(ID, [Type]) VALUES (3, '');
, тоMerge
дляVALUES(3, 'Some Data');
2. @MartinSmith — хотя это постоянная константа…
3. @JNK — Да, я не говорил, что это правильное поведение (оно кажется явно неправильным), я просто сказал, что это то, что, похоже, происходит.
4. @MartinSmith: Я согласен с вашим выводом, хороший улов!
5. Обходным путем, если вы действительно хотите использовать слияние, было бы иметь обычный тип столбца в cars с not null и ограничение по умолчанию, подобное этому
[Type] varchar(5) not null default 'Car'
, а также добавить ограничение проверкиCONSTRAINT Cars_CHK_ValidType CHECK([Type] = 'Car')
. Я думаю, что это привело бы к тому же поведению, что и сохраняемый столбец, и слияние работает просто отлично.
Ответ №1:
Для меня это похоже на определенную ошибку MERGE
.
План выполнения имеет Clustered Index Merge
оператор и должен выводиться [Cars].ID,[Cars].Type
для проверки по Vehicles
таблице.
Эксперименты показывают, что вместо передачи значения "Car"
в качестве Type
значения передается пустая строка. Это можно увидеть, удалив ограничение проверки для транспортных средств, а затем вставив
INSERT INTO dbo.Vehicles(ID, [Type]) VALUES (3, '');
Теперь работает следующее утверждение
MERGE dbo.Cars AS TargetTable
USING
( SELECT 3 AS ID ,
'Some Data' AS OtherData
) AS SourceData
ON SourceData.ID = TargetTable.ID
WHEN NOT MATCHED
THEN INSERT (ID, OtherData)
VALUES(SourceData.ID, SourceData.OtherData);
Но конечным результатом является то, что он вставляет строку, нарушающую ограничение FK.
Автомобили
ID Type OtherData
----------- ----- ----------
3 Car Some Data
Автомобили
ID Type
----------- -----
1 Car
2 Truck
3
Проверка ограничений сразу после этого
DBCC CHECKCONSTRAINTS ('dbo.Cars')
Показывает строку-нарушитель
Table Constraint Where
------------- ------------------- ------------------------------
[dbo].[Cars] [Cars_FK_Vehicles] [ID] = '3' AND [Type] = 'Car'