Solving the SQL Server Multiple Cascade Path Issue with a Trigger (转载)
Problem
I am trying to use the ON DELETE CASCADE option when creating a foreign key on my database, but I get the following error:
Msg 1785, Level 16, State 0, Line 3
Introducing FOREIGN KEY constraint 'FK_Table' on table 'Table' may cause cycles or multiple cascade paths.
Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Msg 1750, Level 16, State 0, Line 3
Could not create constraint. See previous errors.
This tip will look at how you can use triggers to replace the functionality you get from the ON DELETE CASCADE option of a foreign key constraint.
Solution
Let's setup a simple scenario to illustrate the issue. The following database diagram illustrates our table and foreign key layout. You can see from the diagram where we are going to have the issue of multiple cascade paths as there are two paths from the Parent table down to the GrandChild table.

And here is the DDL code we can use to setup this scenario in our database.
-- Table creation logic
--parent table
CREATE TABLE [dbo].[Parent](
[ParentID] [bigint] NOT NULL,
[Data] [varchar](10) NOT NULL,
CONSTRAINT [PK_Parent] PRIMARY KEY CLUSTERED
([ParentID] ASC)
)
GO
-- child table 1
CREATE TABLE [dbo].[Child1](
[Child1ID] [bigint] NOT NULL,
[ParentID] [bigint] NULL,
[Data] [varchar](10) NULL,
CONSTRAINT [PK_Child1] PRIMARY KEY CLUSTERED
([Child1ID] ASC)
)
GO
-- child table 2
CREATE TABLE [dbo].[Child2](
[Child2ID] [bigint] NOT NULL,
[ParentID] [bigint] NULL,
[Data] [varchar](10) NULL,
CONSTRAINT [PK_Child2] PRIMARY KEY CLUSTERED
([Child2ID] ASC)
)
GO
-- grandchild table
CREATE TABLE [dbo].[GrandChild](
[GrandChildID] [bigint] NOT NULL,
[Child1ID] [bigint] NULL,
[Child2ID] [bigint] NULL,
[Data] [varchar](10) NULL,
CONSTRAINT [PK_GrandChild] PRIMARY KEY CLUSTERED
([GrandChildID] ASC)
)
GO
-- foreign key constraint
ALTER TABLE [dbo].[Child1] WITH CHECK
ADD CONSTRAINT [FK_Child1_Parent] FOREIGN KEY([ParentID])
REFERENCES [dbo].[Parent] ([ParentID])
ON DELETE CASCADE
GO
-- foreign key constraint
ALTER TABLE [dbo].[Child2] WITH CHECK
ADD CONSTRAINT [FK_Child2_Parent] FOREIGN KEY([ParentID])
REFERENCES [dbo].[Parent] ([ParentID])
ON DELETE CASCADE
GO
-- foreign key constraint
ALTER TABLE [dbo].[GrandChild] WITH CHECK
ADD CONSTRAINT [FK_GrandChild_Child1] FOREIGN KEY([Child1ID])
REFERENCES [dbo].[Child1] ([Child1ID])
ON DELETE CASCADE
GO
-- foreign key constraint
ALTER TABLE [dbo].[GrandChild] WITH CHECK
ADD CONSTRAINT [FK_GrandChild_Child2] FOREIGN KEY([Child2ID])
REFERENCES [dbo].[Child2] ([Child2ID])
ON DELETE CASCADE
GO
After executing the code above we get the following error and confirm the initial assumption we made after looking at the database diagram, that we have multiple paths down to the GrandChild table.
Msg 1785, Level 16, State 0, Line 3
Introducing FOREIGN KEY constraint 'FK_GrandChild_Child2' on table 'GrandChild' may cause cycles or
multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other
FOREIGN KEY constraints.
Msg 1750, Level 16, State 0, Line 3
Could not create constraint. See previous errors.
To get around this error by creating the foreign key with DELETE NO ACTION, we would end up with orphan records in the GrandChild table every time a DELETE statement is issued against the Parent or Child2 tables. Instead of doing this we are going to create an INSTEAD OF trigger in place of the DELETE CASCADE option. One caveat when using an INSTEAD OF trigger is that you can't have a table with both a DELETE CASCADE foreign key constraint and an INSTEAD OF trigger. If we try to create a trigger on the Child2 table as things are setup now we'll get the following error.
Msg 2113, Level 16, State 1, Procedure DELETE_Child2, Line 8
Cannot create INSTEAD OF DELETE or INSTEAD OF UPDATE TRIGGER 'DELETE_Child2' on table 'dbo.Child2'. This
is because the table has a FOREIGN KEY with cascading DELETE or UPDATE.
To get around this restriction we have to remove the DELETE CASCADE option from the foreign key on the Child2 table before we can add the DELETE CASCADE option to the GrandChild tables foreign key and then add an INSTEAD OF trigger to the Parent table. I like to keep things consistent so having DELETE CASCADE on some of the tables foreign keys and using INSTEAD OF triggers on other tables becomes quite confusing and difficult to manage. Although this approach would work, I think a better solution is to remove the DELETE CASCADE option from all the foreign keys and simply use INSTEAD OF triggers on all of the tables to handle removing the child records. We can use the following code to remove the DELETE CASCADE option from all of the foreign key constraints.
-- drop constraints with DELETE CASCADE option
ALTER TABLE [dbo].[Child1] DROP CONSTRAINT [FK_Child1_Parent]
ALTER TABLE [dbo].[Child2] DROP CONSTRAINT [FK_Child2_Parent]
ALTER TABLE [dbo].[GrandChild] DROP CONSTRAINT [FK_GrandChild_Child1]
GO
-- recreate all foreign keys without DELETE CASCADE option
ALTER TABLE [dbo].[Child1] WITH CHECK
ADD CONSTRAINT [FK_Child1_Parent] FOREIGN KEY([ParentID])
REFERENCES [dbo].[Parent] ([ParentID])
GO
ALTER TABLE [dbo].[Child2] WITH CHECK
ADD CONSTRAINT [FK_Child2_Parent] FOREIGN KEY([ParentID])
REFERENCES [dbo].[Parent] ([ParentID])
GO
ALTER TABLE [dbo].[GrandChild] WITH CHECK
ADD CONSTRAINT [FK_GrandChild_Child1] FOREIGN KEY([Child1ID])
REFERENCES [dbo].[Child1] ([Child1ID])
GO
ALTER TABLE [dbo].[GrandChild] WITH CHECK
ADD CONSTRAINT [FK_GrandChild_Child2] FOREIGN KEY([Child2ID])
REFERENCES [dbo].[Child2] ([Child2ID])
GO
Now that we no longer have any foreign keys with the DELETE CASCADE option set we can add INSTEAD OF triggers to each table to handle the deletion of the child records. Please take note of the fact that because we are using an INSTEAD OF trigger we also have to remove the records from the parent table itself after the records have been removed from the child tables. Here is the trigger code.
CREATE TRIGGER [DELETE_Parent]
ON dbo.[Parent]
INSTEAD OF DELETE
AS
BEGIN
SET NOCOUNT ON;
DELETE FROM [Child1] WHERE ParentID IN (SELECT ParentID FROM DELETED)
DELETE FROM [Child2] WHERE ParentID IN (SELECT ParentID FROM DELETED)
DELETE FROM [Parent] WHERE ParentID IN (SELECT ParentID FROM DELETED)
END
GO
CREATE TRIGGER [DELETE_Child1]
ON dbo.[Child1]
INSTEAD OF DELETE
AS
BEGIN
SET NOCOUNT ON;
DELETE FROM [GrandChild] WHERE Child1ID IN (SELECT Child1ID FROM DELETED)
DELETE FROM [Child1] WHERE Child1ID IN (SELECT Child1ID FROM DELETED)
END
GO
CREATE TRIGGER [DELETE_Child2]
ON dbo.[Child2]
INSTEAD OF DELETE
AS
BEGIN
SET NOCOUNT ON;
DELETE FROM [GrandChild] WHERE Child2ID IN (SELECT Child2ID FROM DELETED)
DELETE FROM [Child2] WHERE Child2ID IN (SELECT Child2ID FROM DELETED)
END
GO
To test that everything is working correctly we can run the following script and verify that all the child records are being removed as expected.
INSERT INTO Parent VALUES (1,'test')
INSERT INTO Parent VALUES (2,'test')
INSERT INTO Parent VALUES (3,'test')
INSERT INTO Parent VALUES (4,'test')
INSERT INTO Child1 VALUES (1,1,'test')
INSERT INTO Child2 VALUES (10,2,'test')
INSERT INTO Child1 VALUES (2,3,'test')
INSERT INTO Child2 VALUES (11,4,'test')
INSERT INTO GrandChild VALUES (1,1,null,'test')
INSERT INTO GrandChild VALUES (2,null,10,'test')
INSERT INTO GrandChild VALUES (3,2,null,'test')
INSERT INTO GrandChild VALUES (4,null,11,'test')
DELETE FROM Parent WHERE ParentID=1
DELETE FROM Parent WHERE ParentID=2

Solving the SQL Server Multiple Cascade Path Issue with a Trigger (转载)的更多相关文章
- 灵活运用 SQL SERVER FOR XML PATH 转
灵活运用 SQL SERVER FOR XML PATH FOR XML PATH 有的人可能知道有的人可能不知道,其实它就是将查询结果集以XML形式展现,有了它我们可以简化我们的查询语句实现一些 ...
- SQL Server FOR XML PATH 语句的应用---列转行
经常在论坛看到高手使用了 for xml path,由于是搜索一下,记录了详细的使用方法.在SQL Server中利用 FOR XML PATH 语句能够把查询的数据生成XML数据,下面是它的一些应用 ...
- 使用SQL SERVER FOR XML PATH将多个结果集转换成一行并进行去重处理
在一个医药行业的系统中需要根据患者的接触记录ID获取不同接触类型的集合,效果像这样 --患者接触记录信息,一个患者可以有N个不同的接触记录,每个接触记录又有N个接触类型记录 IF OBJECT ...
- 问题:sqlserver有没有类似Oracle的LISTAGG;结果: 灵活运用 SQL SERVER FOR XML PATH
灵活运用 SQL SERVER FOR XML PATH FOR XML PATH 有的人可能知道有的人可能不知道,其实它就是将查询结果集以XML形式展现,有了它我们可以简化我们的查询语句实现一些以前 ...
- 如何对SQL Server 2005进行设置以允许远程连接(转载)
如何对SQL Server 2005进行设置以允许远程连接(转载) 在尝试从远程计算机连接到 Microsoft SQL Server 2005 实例时,可能会接收到错误消息.在使用任何程序连接到 S ...
- SQL SERVER将多行数据合并成一行(转载)
昨天遇到一个SQL Server的问题:需要写一个储存过程来处理几个表中的数据,最后问题出在我想将一个表的一个列的多行内容拼接成一行 比如表中有两列数据 : ep_classes ep_name A ...
- SQL Server 磁盘空间告急(磁盘扩容)转载
一.背景 在线上系统中,如果我们发现存放数据库文件的磁盘空间不够,我们应该怎么办呢?新买一个硬盘挂载上去可以嘛?(linux下可以直接挂载硬盘进行扩容),但是我们的SQL Server是运行在Wind ...
- SQL Server 2012 OFFSET/FETCH NEXT分页示例(转载)
原文:http://beyondrelational.com/modules/29/presentations/483/scripts/12983/sql-server-2012-server-sid ...
- SQL Server中使用convert进行日期转换(转载)
一般存入数据库中的时间格式为yyyy-mm-dd hh:mm:ss 如果要转换为yyyy-mm-dd 短日期格式.可以使用convert函数.下面是sqlserver帮助中关于convert函数的声 ...
随机推荐
- typedef在C和C++的区别?
一.struct定义结构体1.先声明结构体类型再定义变量名struct name{ member ..};name A;... 如:struct student{ int a;};student st ...
- 前端组件化Polymer入门教程(7)——Local DOM
DOM元素的创建和管理被称为本地DOM(Local DOM) 本地DOM模板 如果你需要使用本地DOM,你们需要用<dom-module>并指定一个相匹配的ID. <dom-modu ...
- 26-hadoop-hbase简介
hadoop的生态系统 1, hbase简介 –HBase–HadoopDatabase,是一个高可靠性.高性能.面向列.可伸缩.实时读写的分布式数据库 –利用HadoopHDFS作为其文件存储系统, ...
- 线上问题定位--CPU100%
服务器CPU突然告警,如何定位是哪个服务进程导致CPU过载,哪个线程导致CPU过载,哪段代码导致CPU过载? 步骤一.找到最耗CPU的进程 工具:top 方法: 执行top -d 1 -c,每秒刷新一 ...
- 软件架构设计学习总结(18):MVC三层架构在各框架(jsp+servlet + Struts1+ Struts2+ springMVC)中的特征
1.基于web开发中最原始的jsp+Servlet 图形化理解jsp+servlet结构: 1.从结构上分析jsp+servlet图解原理: 在基于mvc设计模式下的最原始的jsp+Servlet ...
- 每天一个linux命令(目录)
转:http://www.cnblogs.com/peida/archive/2012/12/05/2803591.html 开始详细系统的学习linux常用命令,坚持每天一个命令,所以这个系列为每天 ...
- nginx 代理之修改header 的HOST,实现代理转代理
现有一个需求,需要从nginx跳转到k8s的traefik代理上,从而实现服务的访问,用于测试.直接修改proxy_set_header的HOST,修改为traefuk代理的域名,proxy_pass ...
- multimap多重映照容器(常用的方法总结)
multimap和map的不同之处在于前者允许重复键值的元素出现. /*关于C++STL中mulitmap的学习,与map不同的是,multimap允许插入重复键值的元素*/ #include < ...
- VS2012 编译报错:找不到编译动态表达式所需的一个或多个类型。是否缺少引用?
今天编译公司项目,原本项目是3.5,由于现在要用到dynamic ,把target 改为4.0 ,编译时 报错误 “找不到编译动态表达式所需的一个或多个类型.是否缺少引用?”,然后根据另一个提示排错 ...
- 提供PPT嵌入Winform/WPF解决方案,Winform/WPF 中嵌入 office ppt 解决方案
Winform/WPF 中嵌入 office ppt(powerpoint)解决方案示: 1. 在winform中操作ppt,翻页.播放.退出:显示 总页数.当前播放页数 2. 启动播放ppt时录制视 ...