SQL Server Trigger for recursive delete on a cascade

狂风中的少年 提交于 2020-02-24 09:07:19

问题


I have a table which contains a tree like structure

It takes the form of:

-nodeID,fkID,parentNode

(parentNode is NULL if it is the root and the nodeID of its parent if it is a child) (fkID is NULL if it is not the root)

the fkID is a FK which when deleted in another table cascades its delete to this table. This cascading delete only however references the root node. There is another constraint put on the database in which a root node cannot be deleted unless its children are deleted first. However I can't cascade on the self referencing constraint because SQL SERVER dosen't give me that option. I thought a trigger might be a good solution however I have to recurse down the tree first and delete the children before the parents. which would require me to delete before the cascade happens. Is there a good way to do this?

I've gotten the following error on the following trigger

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

Thank you in advance for any advice or help!


回答1:


Even though I have voted to close as duplicate, I think I'm going to post an answer, because, on a second thought, the duplicate question's answer ended up being a little bit confusing after the question edits, and the lately added statement that

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

is actually not true (SQL Server will raise an error on a delete, because cascade on one field will conflict with no action on another field).

The main points remain though:

  • You declare both foreign keys as on delete no action.
  • You create an instead of delete trigger on the "main" table that will delete required children, in order, and then delete the "main" record itself.

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;


来源:https://stackoverflow.com/questions/24540860/sql-server-trigger-for-recursive-delete-on-a-cascade

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!