2014-07-02 77 views
2

我有一個包含樹狀結構表SQL Server觸發器遞歸在級聯刪除

它採用的形式:

-nodeID,fkID,parentNode

(parentNode爲NULL如果它是它的父節點的根,並且它是子節點) (如果它不是根,fkID爲NULL)

fkID是一個FK,當它在另一個表中刪除時,將其刪除級聯到該表中。然而,這種級聯刪除僅引用根節點。對數據庫還有一個限制,在該數據庫中,除非首先刪除其子節點,否則無法刪除根節點。但是我無法級聯自引用約束,因爲SQL SERVER不能給我這個選項。我認爲觸發器可能是一個很好的解決方案,但是我必須首先對樹進行遞歸,並在父母面前刪除孩子。這將需要我在級聯發生之前刪除。有沒有一個好的方法來做到這一點?

我已經收到以下錯誤以下觸發

SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
CREATE TRIGGER dbo.deleteChildren 
ON dbo.faultTreeNodes 
INSTEAD OF DELETE 
AS 
BEGIN 
SET NOCOUNT ON; 
    -- Insert statemets for trigger here 
END 
GO 

錯誤:

CANNOT CREATE TRIGGER INSTEAD OF DELETE BECAUSE THIS 
TABLE HAS A FOREIGN KEY WITH A CASCADING DELETE 

預先感謝您的任何建議或幫助!

+0

我想,如果我是你的鞋子,你所遇到的問題會讓我重新考慮數據模型。非常有趣的問題,但。 – DMason

+0

[SQL Server:自引用FK,觸發器而不是ON DELETE CASCADE]的可能重複(http://stackoverflow.com/questions/1783700/sql-server-self-reference-fk-trigger-instead-of-on -delete級聯) – GSerg

回答

0

儘管我已經投票結束爲duplicate,但我想我會發佈一個答案,因爲在第二個想法中,重複問題的答案最終在編輯問題後變得有點令人困惑,並且最近添加的聲明

I guess you just need to drop that ON DELETE CASCADE flag from your recursive foreign key in Categories. The CASCADE flag on the foreign key from CAT_SCH should not matter

實際上是不正確的(SQL Server將產生一個錯誤上刪除,因爲cascade在一個領域將no action上的另一場衝突)。

要點保持,但:

  • 您作爲on delete no action同時聲明外鍵。
  • 您在「主」表上創建一個instead of delete觸發器,它將按順序刪除所需的子項,然後刪除「主」記錄本身。

例如, (SQL Fiddle):

create table main(id int not null primary key); 

create table nodes (
    nodeID int not null primary key, 
    fkID int null foreign key references main(id), 
    parentID int null foreign key references nodes(nodeID) 
); 
create trigger dlt on main 
instead of delete 
as 
begin 

    declare @to_delete table (nodeID int not null, level int not null, primary key(level, nodeID)); 

    begin tran; 

    with cte as (
    select n.nodeID, 0 as level 
    from nodes n inner join deleted d on n.fkID = d.id 

    union all 

    select n.nodeID, level + 1 
    from nodes n inner join cte c on n.parentID = c.nodeID 
) 
    insert into @to_delete(nodeID, level) 
    select nodeID, level 
    from cte; 

    declare cur cursor 
    local 
    forward_only 
    read_only 
    for 
    select distinct level from @to_delete order by level desc; 

    open cur; 

    declare @cur_level int; 

    fetch next from cur into @cur_level; 
    while @@fetch_status = 0 
    begin 
    delete from nodes 
    from nodes n inner join @to_delete d on n.nodeID = d.nodeID 
    where d.level = @cur_level; 

    fetch next from cur into @cur_level; 
    end; 


    close cur; 
    deallocate cur; 

    delete from main from main m inner join deleted d on m.id = d.id; 

    commit tran; 
end;