Foreign Key constraint failure and error mesage when inserting values - sql-server

Hopefully someone can help. I have created two tables Customer and Order as follows;
CREATE TABLE Customer
CustomerID int NOT NULL PRIMARY KEY
CustomerName varchar(25)
The other columns in Customer table are not relevant to my question, so I will not include them here. My CustomerID numbers are from 1 through to 15, all unique.
The second table I created is Orders as follows
CREATE TABLE Orders
OrderID smallint NOT NULL PRIMARY KEY
OrderDate date NOT NULL
CustomerID int FOREIGN KEY REFERENCES Customer (CustomerID);
My insert values is as follows
INSERT INTO Orders (OrderID, OrderDate, CustomerID)
VALUES
(1001, '2008-10-21', 1),
(1002, '2008-10-21', 8),
(1003, '2008-10-22', 15),
(1004, '2008-10-22', 5),
(1005, '2008-10-24', 3),
(1006, '2008-10-24', 2),
(1007, '2008-10-27', 11),
(1008, '2008-10-30', 12),
(1009, '2008-11-05', 4),
(1010, '2008-11-05', 1);
When I try to insert my values into the Order table, I get the following error message....
Msg 547, Level 16, State 0, Line 1.....The INSERT statement conflicted
with the FOREIGN KEY constraint "FK__OrderT__Customer__2D27B809". The
conflict occurred table "dbo.Customer", column 'CustomerID'. The
statement has been terminated.
The numbers for CustomerID in my Order table, are (1; 1; 2; 3; 4; 5; 8; 11; 12 and 15). Therefore I have checked that all my CustomerID numbers in Order table are also in the Customer table.
So my questions are
1) Has the insert values failed because my CustomerID column in Customer table in NOT NULL and I in error made CustomerID column NULL in Order.
2) If the answer to the above question is yes, then is it possible for me to (a) drop the foreign key on the CustomerID column in Order (b) change the column to NOT NULL and (c) then add the foreign key constraint again to this column and then insert the values again?
It might be easier to drop and re-create the table Order. But I am curious if option 2 would work, re dropping and adding a foreign key on the same column.
Hopefully I am on the right track with why I think the error occurred, feel
to correct me if I am wrong.
Thanks everyone
Josie

1) It should be NOT NULL in both. However error is because you attempted to insert a CustomerId that is not in Customer table.
2) You can simply alter the table and make it NOT NULL (error was not that).
Sample:
CREATE TABLE Customer
(
CustomerID INT NOT NULL
PRIMARY KEY ,
CustomerName VARCHAR(25)
);
CREATE TABLE Orders
(
OrderID INT NOT NULL
PRIMARY KEY ,
OrderDate DATE NOT NULL ,
CustomerID INT FOREIGN KEY REFERENCES Customer ( CustomerID )
);
INSERT [Customer] ( [CustomerID], [CustomerName] )
VALUES ( 1, 'Customer 1' ),
( 2, 'Customer 2' ),
( 3, 'Customer 3' ),
( 4, 'Customer 4' ),
( 5, 'Customer 5' ),
( 6, 'Customer 6' );
INSERT [Orders] ( [OrderID], [OrderDate], [CustomerID] )
VALUES
( 1, GETDATE(), 1 ),
( 2, GETDATE(), 2 ),
( 3, GETDATE(), 3 ),
( 4, GETDATE(), 4 ),
( 5, GETDATE(), 5 ),
( 6, GETDATE(), 6 );
INSERT [Orders] ( [OrderID], [OrderDate], [CustomerID] )
VALUES ( 7, GETDATE(), 7 );
Last one would error, because Customer with CustomerID 7 doesn't exist.
Update: I later saw your sample insert. You can find the offending ID like this:
DECLARE #ids TABLE ( id INT );
INSERT #ids ( [id] )
VALUES ( 1 ),
( 8 ),
( 15 ),
( 5 ),
( 3 ),
( 2 ),
( 11 ),
( 12 ),
( 4 ),
( 1 );
SELECT *
FROM #ids AS [i]
WHERE id NOT IN ( SELECT CustomerID
FROM [Customer] AS [c] );

1) Has the insert values failed because my CustomerID column in
Customer table in NOT NULL and I in error made CustomerID column NULL
in Order.
No. The error is not related to allowing NULL in the Order table. A NULL value will be allowed and not checked for referential integrity.
The foreign key violation error means you are attempting to insert a non-NULL CustomerID value into the Order table that does not exist in Customer. If you are certain the CustomerID values exist, perhaps the column mapping is wrong. Try specifying an explicit column list on the INSERT statement.

This error happens whern you are trying to insert a value in foreign key column, which this value does not exists in it's parent table. for example you are trying to insert value X to CustomerId in Order table, which this value does not exists in Customer table. This error occurred because we need to have a good strategy for Referential Integrity. So the only you need to do, is to check your new values(which you are going to insert them into table) to find out that is there any value compromising this rule or not.
However if you want to get an answer for your second question, you can try the below script:
create table t1
(
Id int primary key,
Name varchar(50) null
)
create table t2
(
Id int,
FK int null foreign key references t1(id)
)
go
alter table t2
alter column FK int not null

The identity column is set up once when you create the table.
The id assigned by an identity column start by the seed and are never reused.
So if you find that your customer ids starts from 6, it means that in the past you have added 5 customers and removed it.
If, for any reason, you want to use fixed Id don't use identity. In that case you take full responsability to set a unique value.
I suggest to never rely on fixed ids, if you must add orders from a script use the CustomerName (if unique), or any natural unique key.
You could use a script like this
DECLARE #newOrders TABLE (OrderID INT, CustomerName VARCHAR(25), OrderDate DATE);
INSERT INTO #newOrders (OrderID, CustomerName, OrderDate) VALUES
(1001, 'some-username', '2008-10-21'),
(1002, 'another-username', '2008-10-21');
INSERT INTO Orders(OrderID, CustomerId, OrderDate)
SELECT
o.OrderID,
c.CustomerID,
o.OrderDate
FROM #newOrders o
JOIN Customer c
ON c.CustomerName = o.CustomerName;
In this way you insert the correct CustomerID.
Note that in very rare cases (think twice to use it) you could insert values in identity colums using SET IDENTITY_INSERT statement.

Related

How to Create Shortcut Between Tables by Use of Foreign Keys

I am trying to figure out a method of creating a shortcut between three tables .
I have a master table, A, a table that references the first table, B, and a table that references B, C. I want to create a shortcut between A and C so I don't have to use table B.
In the below example, I want to ensure that the foreign keys FK_A_ID1 and FK_A_ID2 are always equal to each other, causing it to fail when the last insert statement is executed.
CREATE TABLE A (
ID int unique identity,
num int)
CREATE TABLE B (
ID int unique identity,
A_ID int NOT NULL,
CONSTRAINT FK_A_ID1 FOREIGN KEY (A_ID) REFERENCES A (ID))
CREATE TABLE C (
ID int unique identity,
A_ID int NOT NULL,
B_ID int NOT NULL,
CONSTRAINT FK_A_ID2 FOREIGN KEY (A_ID) REFERENCES A (ID),
CONSTRAINT FK_B_ID FOREIGN KEY (B_ID) REFERENCES B (ID))
INSERT INTO A VALUES (0);
DECLARE #A1 int = SCOPE_IDENTITY();
INSERT INTO A VALUES (1);
DECLARE #A2 int = SCOPE_IDENTITY();
INSERT INTO B Values (#A1);
DECLARE #B1 int = SCOPE_IDENTITY();
INSERT INTO C Values (#A2, #B1);
Is this possible by use of foreign keys or is there another built-in function that I don't know of?
The goal of this is to have a reliable 'shortcut' between tables A and C
One way to do this is with triggers. A trigger can do the joins necessary to ensure C.A = C.B->B.A. We use this method to verify parent keys match up between parent, child, and grandchild tables.
For example:
-- untested code
create trigger C_IU_Verify_A on C
for insert, update as
if exists
(
select 1
from inserted
inner join b on b.id = inserted.b_id
where b.a_id <> inserted.a_id
)
begin
raiserror('Parent table keys do not match.', 16, 1)
rollback
end
Another way to do this is with compound primary keys. Define the primary key of B as (a_id, id). Setup a foreign key from B(a_id) to A(id). Setup a second foreign key from C(a_id, b_id) to B(a_id, id). At this point, you have referential integrity between C.a_id and B.a_id.
For example:
create table a (id int primary key clustered)
create table b(a_id int, id int, primary key (a_id, id), unique(id))
create table c(a_id int, b_id int, id int, primary key (a_id, b_id, id), unique(id))
alter table B add constraint fk_b_aid foreign key (a_id) REFERENCES A(id)
alter table C add constraint fk_c_aid_bid foreign key (a_id, b_id) REFERENCES B(a_id, id)
insert into a (id) select 1
insert into a (id) select 2
insert into b (a_id, id) select 1, 1
insert into b (a_id, id) select 1, 2
--insert into b (a_id, id) select 2, 1 -- error: duplicate b.id
insert into b (a_id, id) select 2, 3
--insert into b (a_id, id) select 3, 1 -- error: there is no A with id = 3
insert into c (a_id, b_id, id) select 1, 1, 1
insert into c (a_id, b_id, id) select 1, 1, 2
insert into c (a_id, b_id, id) select 1, 2, 3
insert into c (a_id, b_id, id) select 2, 3, 4
--insert into c (a_id, b_id, id) select 1, 3, 5 -- error: there is no B with B.a_id = 1 and B.id = 3
drop table c;
drop table b;
drop table a;
I'm reasonably sure the following does what you want. Using psuedo code:
-- Has a primary key
CREATE TABLE A
(
A_id PrimaryKey
)
-- Has both a primary key and a compound unique constraint, as well as a foreign key to A
CREATE TABLE B
(
B_id PrimaryKey Unique_1of2
,A_id Unique_2of2 ForeignKey_to_A
)
-- Has a primary key and a compound foreign key to B
CREATE TABLE C
(
C_id PrimaryKey
,B_id ForeignKey_to_B_1of2
,A_id ForeignKey_to_B_2of2
)
Done this way:
It is not possible to put a value for A_id in B that is not also in A
it is not possible to put a values for A_id and B_id in table C that are not also a pair in B
NULL values may or may not mess with this--if you have to deal with them as well, you have bigger problems.

Can't insert data for 2 foreign key for the same primary key in SQL Server

I can't insert data in a table where 2 foreign keys refer to one primary key...
The code is as follows:
create table Currency
(
ID int primary key identity(1,1),
Code nvarchar(8) not null,
Name nvarchar(128) not null,
Is_Active bit not null default(0),
Is_Base_Currency bit not null default(0),
Country_id int foreign key(ID) references Country(ID) not null
)
Create table Currency_rate
(
ID int primary key identity(1,1),
Currency_id int foreign key(ID) references Currency(ID) not null,
Base_currency_id int foreign key(ID) references Currency(ID) not null,
Rate decimal(16,6) not null,
Ts datetime default getDate()
)
Insert into Currency_rate(Currency_id, Base_currency_id, Rate, Ts)
values (1, 1, 121212.212121, '2008-11-11 13:23:44.111'),
(2, 2, 232323.323232, '2009-11-11 13:23:44.222'),
(3, 3, 343434.434343, '2010-11-11 13:23:44.333')
This is the error I get:
Msg 547, Level 16, State 0, Line 1
The INSERT statement conflicted with the FOREIGN KEY constraint "FK__Currency___Curre__239E4DCF". The conflict occurred in database "CryptoCurrencyData", table "dbo.Currency", column 'ID'.
The statement has been terminated.
Please help me - I can't find any solution surfing the internet...
Thank you all,
Regards,
Elias.H
Constrainsts are created to prevent corrupting data by inserting or updating your tables.
In your case, you are trying to insert data which is not existing. You are trying to insert into Currency_rate values ID's of Currency which are not existing in Currency table. So this is a whole goal of constraints - prevent corruption of data.
Just for demonstration purposes I've created Country table:
Create table Country (
ID int primary key identity(1,1),
CountryName nvarchar(50)
)
Then your first step would be:
INSERT INTO dbo.Country
(
--ID - this column value is auto-generated
CountryName
)
VALUES
(
-- ID - int
N'India' -- CountryName - nvarchar
)
, (N'Canada')
, (N'South America')
The second step will be:
INSERT INTO dbo.Currency
(
--ID - this column value is auto-generated
Code,
Name,
Is_Active,
Is_Base_Currency,
Country_id
)
VALUES
(
-- ID - int
N'Code1', -- Code - nvarchar
N'India Currency', -- Name - nvarchar
0, -- Is_Active - bit
0, -- Is_Base_Currency - bit
1 -- Country_id - int
)
, (
N'Code2', -- Code - nvarchar
N'Canada Currency', -- Name - nvarchar
0, -- Is_Active - bit
0, -- Is_Base_Currency - bit
2 -- Country_id - int
)
, (
N'Code3', -- Code - nvarchar
N'South America Currency', -- Name - nvarchar
0, -- Is_Active - bit
0, -- Is_Base_Currency - bit
3 -- Country_id - int
)
And the final step is the following:
Insert into Currency_rate(Currency_id,Base_currency_id,Rate,Ts)
values(1,1,121212.212121,'2008-11-11 13:23:44.111'),
(2,2,232323.323232,'2009-11-11 13:23:44.222'),
(3,3,343434.434343,'2010-11-11 13:23:44.333')

Best Way to Store Hierarchical Items of Different Types in SQL Server

The project I am working on has 7 levels to their business hierarchy. None of them are of the same type. Meaning, this is not an organizational chart and all of the items are Employees of some level or other. They are things like Division, Region, Sales VP, Business Unit and such. Yes, some of them are perhaps Employees, but not all of them.
Currently, I have them each in their own table that follow a similar pattern to each other where each child has a foreign key to their parent. So starting with the smallest part of the hierarchy:
BusinessUnit (table)
ID
Name
AreaManagerID
AreaManager (table)
ID
Name
RegionalManagerID
RegionalManager (table)
ID
Name
DivisionID
Division (table)
ID
Name
There are 3 more tables intermixed, but this should show you the rather simple link between each level of the hierarchy. Every child must have a parent. There will not be any AreaManager that has no BusinessUnits.
Reading up a bit on the HierarchyID I am not totally sure it will help me.
I know the above works and it is fine. But I am more wondering if there is a better way and/or faster way when I am tasked with being given a Division and need to find all of the BU's within it. Or even being given a Region and needing to find all of the BU's within it.
If you're looking for "get me descendants", HierarchyID is pretty fast at finding descendants to an arbitrary depth. If you were to do that, I'd put all of the entities into one table with break out tables for the different types. It would look a little something like this:
CREATE TABLE [dbo].[BusinessEntity] (
[EntityID] INT IDENTITY NOT NULL PRIMARY KEY,
[ParentEntityID] INT
REFERENCES [dbo].[BusinessEntity] ([EntityID]),
[EntityType] TINYINT NOT NULL,
[Path] HIERARCHYID
);
CREATE TABLE [dbo].[BusinessUnit] (
[ID] INT NOT NULL PRIMARY KEY
REFERENCES [dbo].[BusinessEntity] ([EntityID]),
[Name] VARCHAR(255) NOT NULL,
[AreaManagerID] INT NOT NULL
);
CREATE TABLE [dbo].[AreaManager] (
[ID] INT NOT NULL PRIMARY KEY
REFERENCES [dbo].[BusinessEntity] ([EntityID]),
[Name] VARCHAR(255) NOT NULL,
[RegionalManagerID] INT NOT NULL
);
CREATE TABLE [dbo].[RegionalManager] (
[ID] INT NOT NULL PRIMARY KEY
REFERENCES [dbo].[BusinessEntity] ([EntityID]),
[Name] VARCHAR(255) NOT NULL,
[DivisionID] INT NOT NULL
);
CREATE TABLE [dbo].[Division] (
[ID] INT NOT NULL PRIMARY KEY
REFERENCES [dbo].[BusinessEntity] ([EntityID]),
[Name] VARCHAR(255)
);
When you go to insert into one of your actual tables (e.g. BusinessUnit, RegionalManager, etc), you'd first create a record in BusinessEntity and then use the generated identity value as the identifier for the insert. You'll also need to keep the Path column up to date with respect to its relationship in the hierarchy. That is, let's say that I have the following data in BusinessEntity:
SET IDENTITY_INSERT [dbo].[BusinessEntity] ON;
INSERT INTO [dbo].[BusinessEntity]
( [EntityID],
[ParentEntityID] ,
[EntityType]
)
VALUES
(1, NULL, 1),
(2, 1, 2),
(3, 1, 2),
(4, 2, 3),
(5, 3, 3),
(6, 4, 4),
(7, 6, 5);
Then I can use the following CTE to generate the Path values
WITH cte AS (
SELECT [be].[EntityID], [be].[ParentEntityID], CAST(CONCAT('/', [be].[EntityID], '/') AS VARCHAR(MAX)) AS [Path]
FROM [dbo].[BusinessEntity] AS [be]
WHERE [be].[ParentEntityID] IS null
UNION ALL
SELECT [child].[EntityID], [child].[ParentEntityID], CAST(CONCAT([parent].[Path], child.[EntityID], '/') AS VARCHAR(MAX))
FROM [dbo].[BusinessEntity] AS [child]
JOIN [cte] AS [parent]
ON [child].[ParentEntityID] = [parent].[EntityID]
)
UPDATE [be]
SET [be].[Path] = cte.[Path]
FROM [dbo].[BusinessEntity] AS be
JOIN cte
ON [be].[EntityID] = [cte].[EntityID]
WHERE [Path] IS NULL;
Of course, keeping them up to date is a lot easier. When you insert a new row, grab the Path from the parent row, tack your ID onto it, and that's your Path. Updating a row's parent is a little trickier, but not terrible. I'll leave it as an exercise for the reader. But as a hint, it involves the GetReparentedValue() method of the HierarchyID data type. Finally, if ever you don't trust the value in Path (as it is a derived value), you can just set whatever values you don't trust to NULL and re-run the above cte update.

SQL Server Trigger functioning backwards

I have a little strange issue with my SQL script and I was hoping someone could help me out with it.
I have a Database being created by using
IF EXISTS (SELECT name
FROM sysdatabases
WHERE name = 'travel')
DROP DATABASE travel
GO
CREATE DATABASE travel
GO
USE travel
GO
I then create 3 tables as shown below
CREATE TABLE customer
(
customerID INT,
lastname VARCHAR(70) NOT NULL,
firstname VARCHAR(70) NOT NULL,
phone VARCHAR(10) CONSTRAINT phoneCheck CHECK ((phone LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]')),
category VARCHAR(7) NOT NULL CONSTRAINT categoryDefault DEFAULT 'A',
CONSTRAINT categoryCheck CHECK (category IN ('A', 'B', 'C')),
CONSTRAINT customerPK
PRIMARY KEY (customerID)
)
CREATE TABLE package /*Still need to do the Zero Padding*/
(
packageCode VARCHAR(6),
destination VARCHAR(70),
CONSTRAINT packageCodeCheck CHECK (packageCode LIKE ('YFK%')),
price MONEY NOT NULL CONSTRAINT priceCheck CHECK ((price BETWEEN 1000 AND 10000)),
passportRequired VARCHAR(7) NOT NULL CONSTRAINT passportRequiredDefault DEFAULT 'Y',
CONSTRAINT passportCheck CHECK (passportRequired IN ('Y', 'N')),
CONSTRAINT packagePK
PRIMARY KEY (packageCode)
)
CREATE TABLE booking /*Still need to do the Customer and Package delete*/
(
customerID VARCHAR(6),
bookingDate VARCHAR(70) NOT NULL DEFAULT GETDATE(),
amountPaid MONEY CONSTRAINT amountPaidDefault DEFAULT 0.00,
CONSTRAINT bookingPK
PRIMARY KEY (customerID)
)
Now heres the issue, I create a trigger as shown below
GO
CREATE TRIGGER customerDelete ON customer AFTER DELETE
AS
DELETE booking
FROM customer
WHERE customer.customerID = booking.customerID
GO
Which to my understanding it will delete all records in booking... that have the matching customerID WHEN a record is deleted from the customer Table. (I am new to triggers)
I INSERT Sample Data as shown below
INSERT INTO customer
(customerID, lastname, firstname, phone, category)
VALUES
(1, 'Picard', 'Corey', 1234567890, 'A'),
(2, 'Bond', 'Devon', 9876543210, 'B'),
(3, 'Douglas', 'Bryan', 6549871230, 'C')
INSERT INTO package
(packageCode, destination, price, passportRequired)
VALUES
('YFK001', 'Toronto', 1000.57, 'N'),
('YFK002', 'Orlando', 3000.98, 'Y')
INSERT INTO booking
(customerID, bookingDate, amountPaid)
VALUES
(1, GETDATE(), 1548),
(2, GETDATE(), 1586),
(3, GETDATE(), 1350),
(4, GETDATE(), 1650)
And Finally I delete the Customer from the TABLE customer with the customerID of 1 by using
DELETE customer
WHERE customerID = 1
However, when I attempt to see the results by using
SELECT * FROM customer
--WHERE customerID = 1 OR customerID = 2 OR customerID = 3
SELECT * FROM package
--WHERE packageCode = 'YFK001' OR packageCode = 'YFK002'
SELECT * FROM booking
--WHERE customerID = 1 OR customerID = 2 OR customerID = 3 OR customerID = 4
It displays bookings with customerID 1 and 4.
Can you let me know what I'm doing wrong?
The trigger is essentially used for the purpose of deleting the bookings with the same customerID of the customer we delete from the customer TABLE
All help is greatly appreciated.
Thanks,
Bryan
Change your delete to this:
DELETE B
FROM booking B
INNER JOIN DELETED D
ON B.customerID = D.customerID;
My Answer is not trigger, [ If you specially want to use Trigger then You can use Lamak's & Pradeep's Answer]
Here best way you can do is use Cacade on foreign Key in your case
Here is your Query I just updated cascade in it
CREATE TABLE customer
(
customerID INT,
lastname VARCHAR(70) NOT NULL,
firstname VARCHAR(70) NOT NULL,
phone VARCHAR(10) CONSTRAINT phoneCheck CHECK ((phone LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]')),
category VARCHAR(7) NOT NULL CONSTRAINT categoryDefault DEFAULT 'A',
CONSTRAINT categoryCheck CHECK (category IN ('A', 'B', 'C')),
CONSTRAINT customerPK
PRIMARY KEY (customerID)
)
CREATE TABLE package /*Still need to do the Zero Padding*/
(
packageCode VARCHAR(6),
destination VARCHAR(70),
CONSTRAINT packageCodeCheck CHECK (packageCode LIKE ('YFK%')),
price MONEY NOT NULL CONSTRAINT priceCheck CHECK ((price BETWEEN 1000 AND 10000)),
passportRequired VARCHAR(7) NOT NULL CONSTRAINT passportRequiredDefault DEFAULT 'Y',
CONSTRAINT passportCheck CHECK (passportRequired IN ('Y', 'N')),
CONSTRAINT packagePK
PRIMARY KEY (packageCode)
ON DELETE CASCADE
ON UPDATE CASCADE
)
CREATE TABLE booking /*Still need to do the Customer and Package delete*/
(
customerID VARCHAR(6),
bookingDate VARCHAR(70) NOT NULL DEFAULT GETDATE(),
amountPaid MONEY CONSTRAINT amountPaidDefault DEFAULT 0.00,
CONSTRAINT bookingPK
PRIMARY KEY (customerID)
ON DELETE CASCADE
ON UPDATE CASCADE
)
This way when you are deleting or update [keyvalue] any entry of Table holding primary key will update its related foreign childs

Is this a bug in MERGE, failing to implement FOREIGN KEY properly?

I am using the following tables to implement subtypes, which is a very common approach:
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');
I have no problem adding a child row via INSERT, as follows:
INSERT INTO dbo.Cars(ID, OtherData)
VALUES(1, 'Some Data');
DELETE FROM dbo.Cars;
Surprisingly, MERGE fails to add one child row:
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.
Is this a bug in MERGE or am I missing something?
Looks like a definite bug in MERGE to me.
The execution plan has the Clustered Index Merge operator and is supposed to output [Cars].ID,[Cars].Type for validation against the Vehicles table.
Experimentation shows that instead of passing the value "Car" as the Type value it is passing an empty string. This can be seen by removing the check constraint on Vehicles then inserting
INSERT INTO dbo.Vehicles(ID, [Type]) VALUES (3, '');
The following statement now works
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);
But the end result is that it inserts a row violating the FK constraint.
Cars
ID Type OtherData
----------- ----- ----------
3 Car Some Data
Vehicles
ID Type
----------- -----
1 Car
2 Truck
3
Checking the constraints immediately afterwards
DBCC CHECKCONSTRAINTS ('dbo.Cars')
Shows the offending row
Table Constraint Where
------------- ------------------- ------------------------------
[dbo].[Cars] [Cars_FK_Vehicles] [ID] = '3' AND [Type] = 'Car'

Resources