SQL对层次结构中的节点重新排序

时间:2020-03-06 14:53:07  来源:igfitidea点击:

我有一个使用邻接列表模型的"任务列表"数据库(请参见下文),因此每个"任务"可以有无限个子任务。该表具有" TaskOrder"列,因此所有内容均以正确的顺序在树视图上呈现。

是否存在一条SQL语句(MS-SQL 2005),该语句将为指定的父级选择所有的子节点,并在删除同级时更新TaskOder列?

Task Table
----------
TaskId
ParentTaskId
TaskOrder
TaskName
--etc--

有任何想法吗?谢谢。

解决方案

如果我们仅使用TaskOrder进行排序,那么简单地在TaskOrder中留下漏洞肯定会更简单,因为仅删除项目不会使排序不正确。但是,我不确定应用程序的需求。

不直接。这是一种拓扑排序,我们可以在其中将子节点"悬挂"在父节点上。如果子代之间没有依赖性,则执行子代的顺序无关紧要。如果必须按一定顺序执行子级,则我们没有足够的信息来推断出它们将必须具有添加的层次结构级别。

假设父母中孩子的顺序无关紧要,那么政治上的排序将为我们提供所需的东西。在大多数SQL方言中,我们都不会将其放入单个查询中,而必须编写一个sproc来完成。

如果节点中子级的顺序相关,那么我们需要在父级中维护任务顺序。使用ParentNodeID,TaskOrder和count(*)进行的查询将挑选出重复项,但除非系统具有其他信息以对任务进行排序,否则我们仍然需要手动干预才能选择正确的顺序。

如果我们想让我澄清一下,请添加评论。

这看起来像是ROW_Number的工作。

DECLARE @Tasks TABLE
(
  TaskId int PRIMARY KEY,
  ParentTaskId int,
  TaskOrder int,
  TaskName varchar(30)
)

INSERT INTO @Tasks(TaskId, ParentTaskId, TaskOrder, TaskName)
SELECT 1, null, 1, 'ParentTask'

INSERT INTO @Tasks(TaskId, ParentTaskId, TaskOrder, TaskName)
SELECT 2, 1, 2, 'B'

INSERT INTO @Tasks(TaskId, ParentTaskId, TaskOrder, TaskName)
SELECT 3, 1, 1, 'A'

INSERT INTO @Tasks(TaskId, ParentTaskId, TaskOrder, TaskName)
SELECT 4, 1, 3, 'C'
--Initial
SELECT * FROM @Tasks WHERE ParentTaskId = 1 ORDER BY TaskOrder

DELETE FROM @Tasks WHERE TaskId = 2
--After Delete
SELECT * FROM @Tasks WHERE ParentTaskId = 1 ORDER BY TaskOrder

UPDATE t
SET TaskOrder = NewTaskOrder
FROM @Tasks t
  JOIN
(
SELECT TaskId, ROW_Number() OVER(ORDER BY TaskOrder) as NewTaskOrder
FROM @Tasks
WHERE ParentTaskId = 1
) sub ON t.TaskId = sub.TaskId

--After Update
SELECT * FROM @Tasks WHERE ParentTaskId = 1 ORDER BY TaskOrder

删除任务88:

UPDATE TaskTable
SET ParentTaskID = (SELECT ParentTaskID AS temp FROM Task_Table t1 WHERE TaskID = 88)
WHERE
TaskID IN (SELECT TaskID task2 FROM TaskTable t2 WHERE ParentTaskID = 88);
Delete FROM TaskTable WHERE TaskID = 88;

当然,我们可以消除删除,而只保留记录以备将来报告之用。

注意:未经测试!!!

几种不同的方式...由于TaskOrder由父ID限定范围,因此收集它并不是很困难。在SQL Server中,我在delete上放置了一个触发器,该触发器使所有的触发器都比删除的触发器"高",从而缩小了差距(后面是伪代码):

CREATE TRIGGER ON yourtable FOR DELETE
AS
  UPDATE Task
     SET TaskOrder    = TaskOrder - 1
   WHERE ParentTaskId = deleted.ParentTaskId
     AND TaskOrder    > deleted.TaskOrder

如果我们不希望使用触发器,则可以先在查询中捕获parentID和TaskOrder,然后删除该行,然后执行相同的update语句,但要使用文字而不是触发器。

或者,如果要最大程度地减少服务器往返次数,可以将要删除的任务一直移到最底端,然后再将其他任务向上移动,然后执行删除操作,但这似乎过于复杂。