Удаление неподключенных узлов из базы данных SQL - PullRequest
1 голос
/ 20 июня 2019

Я работаю над древовидной структурой с иерархией компании.Все данные хранятся в базе данных SQL Server.

В базе данных есть два столбца с идентификатором и идентификатором родительского узла (оба varchar (5) ).

Теперь, когда я удаляю узел, все узлыдети (и их дети) останутся в базе данных - не связаны между собой.Как я должен удалить их?

РЕДАКТИРОВАТЬ: я использую https://www.codeproject.com/Articles/18378/Organization-Chart-Generator для создания диаграммы.

Ответы [ 3 ]

3 голосов
/ 20 июня 2019

Вам нужно 2 вещи здесь,

  • Сначала вам нужна система, которая не позволяет удалить мастер, который имеет Чайлдс.
  • Во-вторых, вам нужна процедура удаления мастера со всеми его потомками.

Первое необходимо, чтобы никто не мог удалить мастер, используя любые другие средства, кроме правильной процедуры.

Вот пример того, как вы можете настроить такую ​​систему

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 остановит вас и вернет ошибку, сообщающую, что внешний ключ fk_pid нарушен.
Другими словами, никто больше не может удалить мастера, если у него все еще есть дети.

Теперь для процедуры, которая может удалить мастера со всеми его детьми, вы можете посмотреть на другие ответы и просто выбрать тот, который вам больше нравится.

2 голосов
/ 20 июня 2019

Вы можете сделать это с помощью рекурсивной процедуры: рекурсивно удалить сначала все дочерние элементы узла, а затем удалить этот узел.

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, поэтому вы не можете удалить узлы, оставив дочерние элементы не подключенными.

2 голосов
/ 20 июня 2019

Если нет внешних ключей, это сделает это.Я создаю пример таблицы с именами узлов с двумя столбцами, 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

Результат тот же.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...