问题
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