2010-01-12 58 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