Удаление иерархических данных в таблице SQL - PullRequest
16 голосов
/ 19 мая 2009

У меня есть таблица с иерархическими данными.
Столбец «ParentId», который содержит идентификатор («ID» - ключевой столбец) своего родителя.

При удалении строки я хочу удалить все дочерние элементы (все уровни вложенности).

Как это сделать?

Спасибо

Ответы [ 8 ]

9 голосов
/ 19 мая 2009

На SQL Server: используйте рекурсивный запрос. Учитывая CREATE TABLE tmp (Id int, Parent int), используйте

WITH x(Id) AS (
    SELECT @Id
    UNION ALL
    SELECT tmp.Id
      FROM tmp
      JOIN x ON tmp.Parent = x.Id
)
DELETE tmp
  FROM x
  JOIN tmp ON tmp.Id = x.Id
5 голосов
/ 19 мая 2009

Добавить ограничение внешнего ключа. Следующий пример работает для MySQL ( ссылка на синтаксис ):

ALTER TABLE yourTable
ADD CONSTRAINT makeUpAConstraintName
FOREIGN KEY (ParentID) REFERENCES yourTable (ID)
ON DELETE CASCADE;

Это будет работать на уровне базы данных, dbms будет гарантировать, что после удаления строки будут удалены и все ссылочные строки.

4 голосов
/ 19 мая 2009

Когда число строк не слишком велико, рекурсивный подход Эриккалена работает.

Вот альтернатива, которая использует временную таблицу для сбора всех дочерних элементов:

create table #nodes (id int primary key)
insert into #nodes (id) values (@delete_id)
while @@rowcount > 0
    insert into #nodes 
    select distinct child.id 
    from table child
    inner join #nodes parent on child.parentid = parent.id
    where child.id not in (select id from #nodes)

delete
from table
where id in (select id from #nodes)

Он начинается со строки с @delete_id и спускается оттуда. Оператор where должен защищать от рекурсии; если вы уверены, что их нет, вы можете оставить это.

3 голосов
/ 19 мая 2009

Зависит от того, как вы храните вашу иерархию. Если у вас есть только ParentID, то это может быть не самый эффективный подход, который вы выбрали. Для облегчения работы с поддеревьями у вас должен быть дополнительный столбец Parents, в котором будут храниться все родительские идентификаторы, например:

/1/20/25/40

Таким образом, вы сможете получить все подузлы просто:

where Parents like @NodeParents + '%'

Второй подход
Вместо только ParentID вы также можете иметь значения left и right. Вставки, делающие это таким образом, работают медленнее, но операции выбора выполняются чрезвычайно быстро. Особенно при работе с узлами поддеревьев ... http://en.wikipedia.org/wiki/Tree_traversal

Третий подход
проверить рекурсивные CTE, если вы используете SQL 2005 +

Четвертый подход
Если вы используете SQL 2008, проверьте тип HierarchyID. Это дает достаточно возможностей для вашего случая. http://msdn.microsoft.com/en-us/magazine/cc794278.aspx

2 голосов
/ 19 мая 2009

Добавьте триггер к таблице следующим образом

создать триггер TD_MyTable на myTable для удаления как - Удалить один уровень детей удалить M из удаленного D внутреннее соединение myTable M по D.ID = M.ID

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

ST

0 голосов
/ 19 мая 2009

Триггеры могут использоваться только для иерархий глубиной 32 или менее:

http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/05/11/defensive-database-programming-fun-with-triggers.aspx

0 голосов
/ 19 мая 2009

То, что вы хотите, это ссылочная целостность между этими таблицами.

0 голосов
/ 19 мая 2009

Зависит от вашей базы данных. Если вы используете Oracle, вы можете сделать что-то вроде этого:

DELETE FROM Table WHERE ID IN (
  SELECT ID FROM Table
  START WITH ID = id_to_delete
  CONNECT BY PRIOR.ID = ParentID
)

ETA:

Без CONNECT BY становится немного сложнее. Как и предполагали другие, ограничение триггера или каскадного удаления, вероятно, будет самым простым.

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