2014-07-02 55 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;