It looks like foreign keys are not being dropped if they are referencing an index and the index needs to be dropped. My settings do specify that foreign keys should be disabled
The comparison happens on the primary key, but the foreign key is linked to a unique index.
Reproduction:
-- Source System
USE tempdb;
GO
SET ANSI_NULLS, QUOTED_IDENTIFIER, ANSI_PADDING ON;
GO
IF EXISTS ( SELECT 1
FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[TableXRef]')
AND type IN (N'U') )
DROP TABLE dbo.TableXRef;
IF EXISTS ( SELECT 1
FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[TableBase]')
AND type IN (N'U') )
DROP TABLE dbo.TableBase;
GO
CREATE TABLE dbo.TableBase (id INT IDENTITY(1, 1)
NOT NULL
, NaturalKey VARCHAR(20) NOT NULL
, CONSTRAINT PK_dbo_TableBase PRIMARY KEY CLUSTERED (id ASC) ON [PRIMARY]
, CONSTRAINT UQ_dbo_TableBase_NaturalKey UNIQUE NONCLUSTERED (NaturalKey ASC) ON [PRIMARY])
ON
[PRIMARY];
GO
CREATE TABLE dbo.TableXRef (id INT IDENTITY(1, 1)
NOT NULL
, NaturalKey VARCHAR(20) NOT NULL
, DummyData CHAR(4) NOT NULL
, CONSTRAINT PK_dbo_TableXRef PRIMARY KEY CLUSTERED (id ASC) ON [PRIMARY]
, CONSTRAINT UQ_dbo_TableBase_DummyData UNIQUE NONCLUSTERED (DummyData ASC) ON [PRIMARY])
ON
[PRIMARY];
GO
ALTER TABLE dbo.TableXRef ADD CONSTRAINT
FK_dbo_TableXRef_NaturalKey_dbo_TableBase_NaturalKey FOREIGN KEY
(
NaturalKey
) REFERENCES dbo.TableBase
(
NaturalKey
);
-- generate fake data
INSERT INTO dbo.TableBase
(NaturalKey
)
SELECT number
FROM master.dbo.spt_values
WHERE number BETWEEN 1 AND 100
GROUP BY number
ORDER BY number;
INSERT INTO dbo.TableXRef
(NaturalKey
, DummyData
)
SELECT MIN(number) AS number
, SOUNDEX(name) AS DummyData
FROM master.dbo.spt_values
WHERE number BETWEEN 1 AND 100
AND SOUNDEX(name) IS NOT NULL
GROUP BY SOUNDEX(name)
ORDER BY number;
code for the destination system including data destruction to trigger data sync
-- Destination system
USE tempdb;
GO
SET ANSI_NULLS, QUOTED_IDENTIFIER, ANSI_PADDING ON;
GO
IF EXISTS ( SELECT 1
FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[TableXRef]')
AND type IN (N'U') )
DROP TABLE dbo.TableXRef;
IF EXISTS ( SELECT 1
FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[TableBase]')
AND type IN (N'U') )
DROP TABLE dbo.TableBase;
GO
CREATE TABLE dbo.TableBase (id INT IDENTITY(1, 1)
NOT NULL
, NaturalKey VARCHAR(20) NOT NULL
, CONSTRAINT PK_dbo_TableBase PRIMARY KEY CLUSTERED (id ASC) ON [PRIMARY]
, CONSTRAINT UQ_dbo_TableBase_NaturalKey UNIQUE NONCLUSTERED (NaturalKey ASC) ON [PRIMARY])
ON
[PRIMARY];
GO
CREATE TABLE dbo.TableXRef (id INT IDENTITY(1, 1)
NOT NULL
, NaturalKey VARCHAR(20) NOT NULL
, DummyData CHAR(4) NOT NULL
, CONSTRAINT PK_dbo_TableXRef PRIMARY KEY CLUSTERED (id ASC) ON [PRIMARY]
, CONSTRAINT UQ_dbo_TableBase_DummyData UNIQUE NONCLUSTERED (DummyData ASC) ON [PRIMARY])
ON
[PRIMARY];
GO
ALTER TABLE dbo.TableXRef ADD CONSTRAINT
FK_dbo_TableXRef_NaturalKey_dbo_TableBase_NaturalKey FOREIGN KEY
(
NaturalKey
) REFERENCES dbo.TableBase
(
NaturalKey
);
-- generate fake data
INSERT INTO dbo.TableBase
(NaturalKey
)
SELECT number
FROM master.dbo.spt_values
WHERE number BETWEEN 1 AND 100
GROUP BY number
ORDER BY number;
INSERT INTO dbo.TableXRef
(NaturalKey
, DummyData
)
SELECT MIN(number) AS number
, SOUNDEX(name) AS DummyData
FROM master.dbo.spt_values
WHERE number BETWEEN 1 AND 100
AND SOUNDEX(name) IS NOT NULL
GROUP BY SOUNDEX(name)
ORDER BY number;
-- destroy some data
DELETE x
FROM dbo.TableXRef x
WHERE id>80
DELETE b
FROM dbo.TableBase b
LEFT JOIN dbo.TableXRef x
ON b.NaturalKey = x.NaturalKey
WHERE x.id IS NULL
Deployment script being generated, please note that the foreign key is being disabled but he unique key is supposed to be dropped, this is what is then causing the failure
/*
Run this script on:
QSQL08OCR.tempdb - This database will be modified
to synchronize it with:
MJ00KVMP.tempdb
You are recommended to back up your database before running this script
Script created by SQL Data Compare version 12.0.32.3340 from Red Gate Software Ltd at 10/14/2016 9:43:52 AM
*/
SET NUMERIC_ROUNDABORT OFF
GO
SET ANSI_PADDING, ANSI_WARNINGS, CONCAT_NULL_YIELDS_NULL, ARITHABORT, QUOTED_IDENTIFIER, ANSI_NULLS, NOCOUNT ON
GO
SET DATEFORMAT YMD
GO
SET XACT_ABORT ON
GO
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
GO
BEGIN TRANSACTION
-- Pointer used for text / image updates. This might not be needed, but is declared here just in case
DECLARE @pv binary(16)
PRINT(N'Drop constraints from [dbo].[TableXRef]')
ALTER TABLE [dbo].[TableXRef] NOCHECK CONSTRAINT [FK_dbo_TableXRef_NaturalKey_dbo_TableBase_NaturalKey]
PRINT(N'Drop unused indexes from [dbo].[TableXRef]')
ALTER TABLE [dbo].[TableXRef] DROP CONSTRAINT [UQ_dbo_TableBase_DummyData]
PRINT(N'Drop unused indexes from [dbo].[TableBase]')
ALTER TABLE [dbo].[TableBase] DROP CONSTRAINT [UQ_dbo_TableBase_NaturalKey]
PRINT(N'Add rows to [dbo].[TableBase]')
SET IDENTITY_INSERT [dbo].[TableBase] ON
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (15, '15')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (16, '16')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (17, '17')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (18, '18')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (19, '19')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (20, '20')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (21, '21')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (22, '22')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (23, '23')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (24, '24')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (25, '25')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (26, '26')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (27, '27')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (28, '28')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (29, '29')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (30, '30')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (31, '31')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (32, '32')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (33, '33')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (34, '34')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (35, '35')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (36, '36')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (37, '37')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (38, '38')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (39, '39')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (40, '40')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (41, '41')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (42, '42')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (43, '43')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (44, '44')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (45, '45')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (46, '46')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (47, '47')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (48, '48')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (49, '49')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (50, '50')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (51, '51')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (52, '52')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (53, '53')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (54, '54')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (55, '55')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (56, '56')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (57, '57')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (58, '58')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (59, '59')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (60, '60')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (61, '61')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (62, '62')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (63, '63')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (64, '64')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (65, '65')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (66, '66')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (67, '67')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (68, '68')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (69, '69')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (70, '70')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (71, '71')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (72, '72')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (73, '73')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (74, '74')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (75, '75')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (76, '76')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (77, '77')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (78, '78')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (79, '79')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (80, '80')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (81, '81')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (82, '82')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (83, '83')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (84, '84')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (85, '85')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (86, '86')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (87, '87')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (88, '88')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (89, '89')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (90, '90')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (91, '91')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (92, '92')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (93, '93')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (94, '94')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (95, '95')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (96, '96')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (97, '97')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (98, '98')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (99, '99')
INSERT INTO [dbo].[TableBase] ([id], [NaturalKey]) VALUES (100, '100')
SET IDENTITY_INSERT [dbo].[TableBase] OFF
PRINT(N'Operation applied to 86 rows out of 86')
PRINT(N'Add rows to [dbo].[TableXRef]')
SET IDENTITY_INSERT [dbo].[TableXRef] ON
INSERT INTO [dbo].[TableXRef] ([id], [NaturalKey], [DummyData]) VALUES (81, '16', 'R525')
INSERT INTO [dbo].[TableXRef] ([id], [NaturalKey], [DummyData]) VALUES (82, '16', 'B210')
INSERT INTO [dbo].[TableXRef] ([id], [NaturalKey], [DummyData]) VALUES (83, '16', 'C423')
INSERT INTO [dbo].[TableXRef] ([id], [NaturalKey], [DummyData]) VALUES (84, '16', 'D110')
INSERT INTO [dbo].[TableXRef] ([id], [NaturalKey], [DummyData]) VALUES (85, '16', 'D236')
INSERT INTO [dbo].[TableXRef] ([id], [NaturalKey], [DummyData]) VALUES (86, '16', 'T650')
INSERT INTO [dbo].[TableXRef] ([id], [NaturalKey], [DummyData]) VALUES (87, '26', 'R165')
INSERT INTO [dbo].[TableXRef] ([id], [NaturalKey], [DummyData]) VALUES (88, '32', 'L352')
INSERT INTO [dbo].[TableXRef] ([id], [NaturalKey], [DummyData]) VALUES (89, '32', 'S263')
INSERT INTO [dbo].[TableXRef] ([id], [NaturalKey], [DummyData]) VALUES (90, '32', 'H133')
INSERT INTO [dbo].[TableXRef] ([id], [NaturalKey], [DummyData]) VALUES (91, '32', 'S640')
INSERT INTO [dbo].[TableXRef] ([id], [NaturalKey], [DummyData]) VALUES (92, '64', 'A631')
INSERT INTO [dbo].[TableXRef] ([id], [NaturalKey], [DummyData]) VALUES (93, '64', 'S332')
INSERT INTO [dbo].[TableXRef] ([id], [NaturalKey], [DummyData]) VALUES (94, '64', 'S616')
INSERT INTO [dbo].[TableXRef] ([id], [NaturalKey], [DummyData]) VALUES (95, '64', 'P600')
SET IDENTITY_INSERT [dbo].[TableXRef] OFF
PRINT(N'Operation applied to 15 rows out of 15')
PRINT(N'Add indexes to [dbo].[TableXRef]')
ALTER TABLE [dbo].[TableXRef] ADD CONSTRAINT [UQ_dbo_TableBase_DummyData] UNIQUE NONCLUSTERED ([DummyData]) ON [PRIMARY]
PRINT(N'Add indexes to [dbo].[TableBase]')
ALTER TABLE [dbo].[TableBase] ADD CONSTRAINT [UQ_dbo_TableBase_NaturalKey] UNIQUE NONCLUSTERED ([NaturalKey]) ON [PRIMARY]
PRINT(N'Add constraints to [dbo].[TableXRef]')
ALTER TABLE [dbo].[TableXRef] WITH CHECK CHECK CONSTRAINT [FK_dbo_TableXRef_NaturalKey_dbo_TableBase_NaturalKey]
COMMIT TRANSACTION
GO
I am using options "Disable foreign keys" and "Drop primary keys, indexes, and unique constraints" enabled.
Anyway, yes, if I do not enable to option to drop/recreate indexes it will work. However, if I do enable to drop/recreate indexes then SQL DataCompare should be smart enough to then also drop/create/check the corresponding foreign keys instead of disable/enable.
And the reason I prefer to drop/create indexes is that I regularly insert/update/delete millions of records via SQL DataCompare and I do not want the indexes to be updated for every single insert/update/delete statement and potentially run into issues with unique constraints or unique indexes.
Comments
Thanks for contacting us and sorry you are having this issue!
If the primary key, index, or unique constraint is the comparison key, it can't be dropped. https://documentation.red-gate.com/disp ... ct+options
That said - what version are you using currently (11.x.x.x)?
Would you mind sending in an example of this issue?
Thanks!
Technical Sales Engineer
Redgate Software
Reproduction: code for the destination system including data destruction to trigger data sync
Deployment script being generated, please note that the foreign key is being disabled but he unique key is supposed to be dropped, this is what is then causing the failure
I am using options "Disable foreign keys" and "Drop primary keys, indexes, and unique constraints" enabled.
Anyway, yes, if I do not enable to option to drop/recreate indexes it will work. However, if I do enable to drop/recreate indexes then SQL DataCompare should be smart enough to then also drop/create/check the corresponding foreign keys instead of disable/enable.
And the reason I prefer to drop/create indexes is that I regularly insert/update/delete millions of records via SQL DataCompare and I do not want the indexes to be updated for every single insert/update/delete statement and potentially run into issues with unique constraints or unique indexes.
It looks complicated!