从SQL数据库中删除未连接的节点

时间:2019-06-20 06:04:51

标签: c# sql-server tree

我正在研究具有公司层次结构的树形结构。所有数据都存储在SQL Server数据库中。

数据库有两列,分别是ID和父节点ID(均为 varchar(5))。

现在,当我删除一个节点时,该节点的所有子节点(及其子节点)将保留在数据库中-未连接。我应该如何删除这些?

编辑:我正在使用https://www.codeproject.com/Articles/18378/Organization-Chart-Generator来生成图表。

3 个答案:

答案 0 :(得分:3)

您在这里需要两件事,

  • 首先,您需要一个防止删除具有以下内容的母版的系统 孩子们。
  • 第二,您需要一个删除母版及其所有子级的过程。

第一个步骤是必需的,因此没有人可以使用其他任何方法然后再按正确的步骤删除母版。

这里是如何设置这样的系统的示例

create table test (
  id int not null identity,
  name varchar(10),
  parentid int  null,

  constraint pk_id primary key (id),  
  constraint fk_pid foreign key (parentid) references test (id) 
)

insert into test (name, parentid) 
values ('master', null), ('child1', 1), ('child2', 1), ('child3', 3)

现在,当您删除例如第一行(name = master)时,sql server将停止您,并返回一条错误消息,指出违反了外键fk_pid
换句话说,只要母版中仍然有孩子,就再也没有人可以移除它。

现在,该程序可以删除一位主人及其所有孩子,您可以查看其他答案,然后选择最喜欢的答案。

答案 1 :(得分:2)

如果没有外键,将执行此操作。我正在创建一个名为节点的示例表,该表具有两列,即ID和Parent。在我的版本中,它们是整数,但没关系。

create table nodes(id int, parent int)
insert nodes values (1,null),(2,1),(3,1),(4,2),(5,null)

select * from nodes

结果是:

id  parent
1   NULL
2   1
3   1
4   2
5   NULL

创建遍历关系的CTE

declare @target int; set @target=2
;with cte as 
(
select *, 1 as depth from nodes where id=@target
union all
select nodes.*, depth+1 from nodes
join cte on cte.id=nodes.parent
)
delete nodes where id in (select id from cte)

这是结果

select * from nodes

1   NULL
3   1
5   NULL

如果您有外键,则需要从最高深度到最低深度进行仔细检查,以免出错。这样做(没有光标)

declare @temp table(id int, depth int)

declare @target int; set @target=2
;with cte as 
(
select *, 1 as depth from nodes where id=@target
union all
select nodes.*, depth+1 from nodes
join cte on cte.id=nodes.parent
)
insert @temp 
select id,depth from cte

while exists(select * from @temp)
begin
    delete nodes from nodes
    join @temp t on t.id=nodes.id
    where depth=(select max(depth) from @temp)

    delete @temp where depth=(select max(depth) from @temp)
end

结果相同。

答案 2 :(得分:2)

您可以使用递归过程来做到这一点:首先递归地删除节点的所有子节点,然后删除该节点。

CREATE PROCEDURE DELETE_NODE
  @NODE_ID int
AS
BEGIN
  declare @CHILD_NODE_ID int;    
  declare CHILDS cursor for select NODE_ID from MY_TABLE where PARENT_NODE_ID = @NODE_ID;

  open CHILDS;
  fetch next from CHILDS into @CHILD_NODE_ID;
  while @@fetch_status = 0 
  begin  
    exec DELETE_NODE @NODE_ID = @CHILD_NODE_ID;

    fetch next from CHILDS into @CHILD_NODE_ID;
  end
  close CHILDS;
  deallocate CHILDS;

  delete from MY_TABLE where NODE_ID = @NODE_ID
END
GO

顺便说一句,您应该在表上为其字段PARENT_NODE_ID和NODE_ID本身添加一个关系(外键),这样就不能删除使子级保持未连接状态的节点。