2010-01-12 38 views
3

我有如下表:我如何误解TSQL中的触发器递归?

if object_id(N'dbo.Node') is null 
create table dbo.Node 
(
    ID bigint identity primary key, 
    ParentID bigint null, -- references Node(ID) 
    DateCreated datetime not null, 
    LastUpdated datetime not null, 
    [Name] nvarchar(500) not null, 
); 

我有这样的触发器来实现级联删除同一表中:

create trigger Node_Delete on Node for delete 
as 
begin 
    delete from Node where ParentID in (select id from deleted) 
end 

这里是我的数据集:

ID     ParentID    DateCreated    LastUpdated    Name 
534     514     2010-01-12 10:15:03.940 2010-01-12 10:15:03.940 Test 1 
535     534     2010-01-12 10:15:08.563 2010-01-12 10:15:08.563 Test 2 
536     535     2010-01-12 10:15:12.063 2010-01-12 10:15:12.063 Test 3 
537     536     2010-01-12 10:15:18.510 2010-01-12 10:15:18.510 Test 4 

现在我执行此查询:

delete from Node where ID=534 

这是得到的数据集:

ID     ParentID    DateCreated    LastUpdated    Name 
536     535     2010-01-12 10:15:12.063 2010-01-12 10:15:12.063 Test 3 
537     536     2010-01-12 10:15:18.510 2010-01-12 10:15:18.510 Test 4 

为什么在触发DELETE语句不会导致触发的递归执行直到所有后代记录被删除?

编辑:请注意,我发布了下面的工作解决方案,但标记了与正确不同的答案,因为我的问题不是“解决方案是什么”,而是“为什么我会这样做不行”。

回答

2

您必须为数据库启用递归触发器。请注意,您只能获得更深的32个级别。

ALTER DATABASE databasename 
SET RECURSIVE_TRIGGERS ON | OFF 
1

因为删除pseudotable仅有一行ID为534

您可以添加一个级联删除的父子关系
或使用CTE来删除所有内容。

+0

您不能在子 - 父关系上添加级联删除,因为SQL Server会抱怨多个级联路径。请注意,我更新了上面的问题。我不明白为什么触发器不会因删除而自行关闭。 – 2010-01-12 10:33:27

+0

哦对。忘了那个。那么你将不得不使用CTE:http://msdn.microsoft.com/en-us/library/ms190766.aspx – 2010-01-12 10:36:12

+0

我可以查看CTE,但我仍然想知道为什么我不能用触发器... – 2010-01-12 10:37:57

1

的进行删除触发删除操作后解雇。因此,任何儿童记录都违反了FK约束,使得删除成为不可能。

您可以通过编写我的博客中描述的INSTEAD OF DELETE trigger来解决此问题。

+0

没有FK约束。 – 2010-01-12 10:52:42

4

我修改触发级联到无限的深度不打开触发器递归。这里是我的解决方案:

create trigger Node_Delete on Node instead of delete 
as 
begin 
    create table #del (id bigint, depth int) 
    declare @depth int 
    set @depth = 1 
    insert into #del select id, @depth from deleted 
    while @@rowcount > 0 
    begin 
     set @depth = @depth + 1 
     insert into #del select id, @depth from Node where ParentID in (select id from #del where depth = @depth-1) 
    end 
    delete from Node where ID in (select id from #del) 
end 

编辑:我现在已经谈妥了使用公共表表达式一个更好的解决方案,如通过以下姆拉登Prajdic另一个答案建议。

create trigger Node_Delete on Node instead of delete 
as 
begin 
    with nodes 
    as 
    (
     select n.ID, n.ParentID, 1 as Level 
     from Node n where n.ID in (select ID from deleted) 
     union all 
     select n.ID, n.ParentID, p.Level+1 as Level 
     from Node n 
     inner join nodes p on p.ID = n.ParentID 
    ) 
    delete from Node where ID in (select ID from nodes); 
end