Hi,
************************************************** *****
* Main problem - cascade deletion of self joins and also child tables. ****
************************************************** *****
I have a schema with a self join in the main table as well as other child tables. Its for a CRM with a recursive data structure where a contact can be a parent of another contact, eg group->company->dept->sales manager.
I prototyped in ms access and thought it would be straight forward to upsize to SQL Server, but how naive I was. I'm newish to SQL server but have done this before in Postgres using 'fk REFERENCES pk on CASCADE DELETE' type of DDL.
I have tried using DTS which doesn't seem to import relationships or RI. I have tried upsizing from MS Access XP using both DRI and triggers, but have problems with both.
Because i plan on replicating the db in the future, i am using GUIDs as PKs throughout.
One immediate problem on upsizing was that any GUID fields were defaulting to NewGuid, which is breaking the RI, because these fk GUIDs were trying to reference a PK which it had just generated a value for. I manually fixed that, by defaulting to NULL and changed the insert trigger to ignore NULLS.
My biggest problem is getting cascade deletions working on the self referenced table. It won't seem to touch it with DRI, but works partially with triggers.
Should i forget DTS and Ms Access upsizing and code the DDL myself, or am I pretty close?
Thanks in advance for any tips ...
AN incomplete DDL script of the table is below:
************** START DDL CONTACTS TABLE *******************
CREATE TABLE [dbo].[Contacts] (
[ContactID] [uniqueidentifier] NOT NULL ,
[CreatorName] [nvarchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[DisplayName] [nvarchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[ParentID] [uniqueidentifier] NULL ,
[ContactType] [uniqueidentifier] NULL ,
[SourceOfInquiry] [uniqueidentifier] NULL ,
[soundex_lastname] [nvarchar] (4) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[soundex_displayname] [nvarchar] (4) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[upsize_ts] [timestamp] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[Contacts] ADD
CONSTRAINT [DF__Contacts__Contac__286302EC] DEFAULT (newid()) FOR [ContactID],
CONSTRAINT [DF__Contacts__Creato__29572725] DEFAULT ('ADMIN') FOR [CreatorName],
CONSTRAINT [DF__Contacts__Parent__2A4B4B5E] DEFAULT (null) FOR [ParentID],
CONSTRAINT [DF__Contacts__Contac__2B3F6F97] DEFAULT (null) FOR [ContactType],
CONSTRAINT [DF__Contacts__Source__2F10007B] DEFAULT (null) FOR [SourceOfInquiry],
CONSTRAINT [aaaaaContacts_PK] PRIMARY KEY NONCLUSTERED
(
[ContactID]
) ON [PRIMARY]
GO
CREATE INDEX [{8CE0F842-5BE0-4860-917D-D2BC0B9C2D50}] ON [dbo].[Contacts]([ParentID]) ON [PRIMARY]
GO
CREATE INDEX [CampaignID] ON [dbo].[Contacts]([SourceOfInquiry]) ON [PRIMARY]
GO
CREATE INDEX [soundex_displayname] ON [dbo].[Contacts]([soundex_displayname]) ON [PRIMARY]
GO
CREATE INDEX [soundex_lastname] ON [dbo].[Contacts]([soundex_lastname]) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Contacts] ADD
CONSTRAINT [Contacts_FK00] FOREIGN KEY
(
[ParentID]
) REFERENCES [dbo].[Contacts] (
[ContactID]
),
CONSTRAINT [Contacts_FK01] FOREIGN KEY
(
[ContactType]
) REFERENCES [dbo].[ContactTypes] (
[ID]
),
CONSTRAINT [Contacts_FK02] FOREIGN KEY
(
[SourceOfInquiry]
) REFERENCES [dbo].[Campaigns] (
[ID]
)
GO
alter table [dbo].[Contacts] nocheck constraint [Contacts_FK00]
GO
alter table [dbo].[Contacts] nocheck constraint [Contacts_FK01]
GO
alter table [dbo].[Contacts] nocheck constraint [Contacts_FK02]
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
CREATE TRIGGER T_Contacts_ITrig
ON Contacts FOR INSERT AS
SET NOCOUNT ON
/* * PREVENT INSERTS IF NO MATCHING KEY IN 'Contacts' */
IF (SELECT ParentID FROM inserted) != NULL
BEGIN
IF (SELECT COUNT(*) FROM inserted) !=
(SELECT COUNT(*) FROM Contacts, inserted WHERE (Contacts.ContactID = inserted.ParentID))
BEGIN
RAISERROR 44447 'The record can''t be added or changed. Referential integrity rules require a related record in table ''Contacts''.'
ROLLBACK TRANSACTION
END
END
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
CREATE TRIGGER T_Contacts_DTrig
ON Contacts FOR DELETE AS
SET NOCOUNT ON
/* * CASCADE DELETES TO 'Opportunities' */
DELETE Opportunities FROM deleted, Opportunities WHERE deleted.ContactID = Opportunities.ContactID
/* * CASCADE DELETES TO 'Journal' */
DELETE Journal FROM deleted, Journal WHERE deleted.ContactID = Journal.ContactID
/* * CASCADE DELETES TO 'GroupMembers' */
DELETE GroupMembers FROM deleted, GroupMembers WHERE deleted.ContactID = GroupMembers.ContactID
/* * CASCADE DELETES TO 'ContactActivities' */
DELETE ContactActivities FROM deleted, ContactActivities WHERE deleted.ContactID = ContactActivities.ContactID
/* * CASCADE DELETES TO 'support_tickets' */
DELETE support_tickets FROM deleted, support_tickets WHERE deleted.ContactID = support_tickets.contact_id
/* * CASCADE DELETES TO 'ContactNotes' */
DELETE ContactNotes FROM deleted, ContactNotes WHERE deleted.ContactID = ContactNotes.ContactID
/* * CASCADE DELETES TO 'Documents' */
DELETE Documents FROM deleted, Documents WHERE deleted.ContactID = Documents.ContactID
/* * CASCADE DELETES TO 'Items' */
DELETE Items FROM deleted, Items WHERE deleted.ContactID = Items.ContactID
/* * RECURSIVE CASCADE DELETES TO 'Contacts' */
DELETE Contacts FROM deleted, Contacts WHERE deleted.ContactID = Contacts.ParentID
***************** END DDL CONTACTS TABLE *******************
************************************************** *****
* Main problem - cascade deletion of self joins and also child tables. ****
************************************************** *****
I have a schema with a self join in the main table as well as other child tables. Its for a CRM with a recursive data structure where a contact can be a parent of another contact, eg group->company->dept->sales manager.
I prototyped in ms access and thought it would be straight forward to upsize to SQL Server, but how naive I was. I'm newish to SQL server but have done this before in Postgres using 'fk REFERENCES pk on CASCADE DELETE' type of DDL.
I have tried using DTS which doesn't seem to import relationships or RI. I have tried upsizing from MS Access XP using both DRI and triggers, but have problems with both.
Because i plan on replicating the db in the future, i am using GUIDs as PKs throughout.
One immediate problem on upsizing was that any GUID fields were defaulting to NewGuid, which is breaking the RI, because these fk GUIDs were trying to reference a PK which it had just generated a value for. I manually fixed that, by defaulting to NULL and changed the insert trigger to ignore NULLS.
My biggest problem is getting cascade deletions working on the self referenced table. It won't seem to touch it with DRI, but works partially with triggers.
Should i forget DTS and Ms Access upsizing and code the DDL myself, or am I pretty close?
Thanks in advance for any tips ...
AN incomplete DDL script of the table is below:
************** START DDL CONTACTS TABLE *******************
CREATE TABLE [dbo].[Contacts] (
[ContactID] [uniqueidentifier] NOT NULL ,
[CreatorName] [nvarchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[DisplayName] [nvarchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[ParentID] [uniqueidentifier] NULL ,
[ContactType] [uniqueidentifier] NULL ,
[SourceOfInquiry] [uniqueidentifier] NULL ,
[soundex_lastname] [nvarchar] (4) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[soundex_displayname] [nvarchar] (4) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[upsize_ts] [timestamp] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[Contacts] ADD
CONSTRAINT [DF__Contacts__Contac__286302EC] DEFAULT (newid()) FOR [ContactID],
CONSTRAINT [DF__Contacts__Creato__29572725] DEFAULT ('ADMIN') FOR [CreatorName],
CONSTRAINT [DF__Contacts__Parent__2A4B4B5E] DEFAULT (null) FOR [ParentID],
CONSTRAINT [DF__Contacts__Contac__2B3F6F97] DEFAULT (null) FOR [ContactType],
CONSTRAINT [DF__Contacts__Source__2F10007B] DEFAULT (null) FOR [SourceOfInquiry],
CONSTRAINT [aaaaaContacts_PK] PRIMARY KEY NONCLUSTERED
(
[ContactID]
) ON [PRIMARY]
GO
CREATE INDEX [{8CE0F842-5BE0-4860-917D-D2BC0B9C2D50}] ON [dbo].[Contacts]([ParentID]) ON [PRIMARY]
GO
CREATE INDEX [CampaignID] ON [dbo].[Contacts]([SourceOfInquiry]) ON [PRIMARY]
GO
CREATE INDEX [soundex_displayname] ON [dbo].[Contacts]([soundex_displayname]) ON [PRIMARY]
GO
CREATE INDEX [soundex_lastname] ON [dbo].[Contacts]([soundex_lastname]) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Contacts] ADD
CONSTRAINT [Contacts_FK00] FOREIGN KEY
(
[ParentID]
) REFERENCES [dbo].[Contacts] (
[ContactID]
),
CONSTRAINT [Contacts_FK01] FOREIGN KEY
(
[ContactType]
) REFERENCES [dbo].[ContactTypes] (
[ID]
),
CONSTRAINT [Contacts_FK02] FOREIGN KEY
(
[SourceOfInquiry]
) REFERENCES [dbo].[Campaigns] (
[ID]
)
GO
alter table [dbo].[Contacts] nocheck constraint [Contacts_FK00]
GO
alter table [dbo].[Contacts] nocheck constraint [Contacts_FK01]
GO
alter table [dbo].[Contacts] nocheck constraint [Contacts_FK02]
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
CREATE TRIGGER T_Contacts_ITrig
ON Contacts FOR INSERT AS
SET NOCOUNT ON
/* * PREVENT INSERTS IF NO MATCHING KEY IN 'Contacts' */
IF (SELECT ParentID FROM inserted) != NULL
BEGIN
IF (SELECT COUNT(*) FROM inserted) !=
(SELECT COUNT(*) FROM Contacts, inserted WHERE (Contacts.ContactID = inserted.ParentID))
BEGIN
RAISERROR 44447 'The record can''t be added or changed. Referential integrity rules require a related record in table ''Contacts''.'
ROLLBACK TRANSACTION
END
END
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
CREATE TRIGGER T_Contacts_DTrig
ON Contacts FOR DELETE AS
SET NOCOUNT ON
/* * CASCADE DELETES TO 'Opportunities' */
DELETE Opportunities FROM deleted, Opportunities WHERE deleted.ContactID = Opportunities.ContactID
/* * CASCADE DELETES TO 'Journal' */
DELETE Journal FROM deleted, Journal WHERE deleted.ContactID = Journal.ContactID
/* * CASCADE DELETES TO 'GroupMembers' */
DELETE GroupMembers FROM deleted, GroupMembers WHERE deleted.ContactID = GroupMembers.ContactID
/* * CASCADE DELETES TO 'ContactActivities' */
DELETE ContactActivities FROM deleted, ContactActivities WHERE deleted.ContactID = ContactActivities.ContactID
/* * CASCADE DELETES TO 'support_tickets' */
DELETE support_tickets FROM deleted, support_tickets WHERE deleted.ContactID = support_tickets.contact_id
/* * CASCADE DELETES TO 'ContactNotes' */
DELETE ContactNotes FROM deleted, ContactNotes WHERE deleted.ContactID = ContactNotes.ContactID
/* * CASCADE DELETES TO 'Documents' */
DELETE Documents FROM deleted, Documents WHERE deleted.ContactID = Documents.ContactID
/* * CASCADE DELETES TO 'Items' */
DELETE Items FROM deleted, Items WHERE deleted.ContactID = Items.ContactID
/* * RECURSIVE CASCADE DELETES TO 'Contacts' */
DELETE Contacts FROM deleted, Contacts WHERE deleted.ContactID = Contacts.ParentID
***************** END DDL CONTACTS TABLE *******************