SQL Server触发器在级联上进行递归删除

时间:2014-07-02 20:54:24

标签: sql sql-server recursion triggers

我有一个包含树状结构的表

采取以下形式:

-nodeID,fkID,parentNode

(如果父节点是根节点,则为NULL,如果是子节点,则为父节点的nodeID) (如果不是根,则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

ERROR:

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

提前感谢您的任何建议或帮助!

1 个答案:

答案 0 :(得分:1)

即使我已经投票结束duplicate,我想我会发布一个答案,因为,在第二个想法,重复问题的答案最终在编辑问题后有点混乱,以及最近添加的声明

  

我猜你只需要从类别中的递归外键中删除ON DELETE CASCADE标志。来自CAT_SCH的外键上的CASCADE标志无关紧要

实际上不是真的(SQL Server会在删除时引发错误,因为一个字段上的cascade会与另一个字段上的no action冲突。)

主要观点仍然存在:

  • 您将两个外键都声明为on delete no action
  • 您在“主”表上创建一个instead of delete触发器,按顺序删除所需的子项,然后删除“主”记录本身。

E.g。 (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;