【问题标题】:SQL to reorder nodes in a hierarchySQL 对层次结构中的节点进行重新排序
【发布时间】:2008-09-29 16:59:47
【问题描述】:

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

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

任务表 ---------- 任务 ID 父任务 ID 任务顺序 任务名称 - 等等 -

有什么想法吗?谢谢。

【问题讨论】:

    标签: sql sql-server hierarchy adjacency-list-model


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      几种不同的方式...由于 TaskOrder 的范围是父 ID,因此收集它并不难。在 SQL Server 中,我会在删除时设置一个触发器,该触发器会减少所有比您删除的“更高”的触发器,从而缩小差距(伪代码如下):

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

      如果您不想要触发器,您可以先在查询中捕获 parentID 和 TaskOrder,删除行,然后执行相同的更新语句,但使用文字而不是触发器。

      或者如果你想尽量减少服务器往返,你可以将要删除的任务一直移动到底部,然后将其他任务向上移动,然后进行删除,但这似乎过于复杂。

      【讨论】:

        【解决方案3】:

        不直接。这是一个Topological Sort,您在其中将子节点“悬挂”在父节点上。如果子级中没有依赖关系,则执行它们的顺序无关紧要。如果子项必须按特定顺序执行,那么您没有足够的信息来推断这一点 - 它们必须具有额外的层次结构。

        假设父母中孩子的顺序无关紧要,那么拓扑排序就会得到你想要的。在大多数 SQL 方言中,您不会将其放入单个查询中 - 您必须编写一个 sproc 来执行此操作。

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

        如果你想让我澄清一些事情,请添加 cmets。

        【讨论】:

          【解决方案4】:

          这看起来像是 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
          

          【讨论】:

            【解决方案5】:

            删除任务 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;
            

            当然,您可以取消删除,只留下记录以备将来报告之用。

            警告:未测试!!!

            【讨论】:

              猜你喜欢
              • 2022-01-08
              • 2020-12-25
              • 2017-08-09
              • 1970-01-01
              • 2013-01-31
              • 2016-05-21
              • 1970-01-01
              • 1970-01-01
              • 2019-12-27
              相关资源
              最近更新 更多