【问题标题】:T-SQL Ordering a Recursive Query - Parent/Child StructureT-SQL 对递归查询进行排序 - 父/子结构
【发布时间】:2015-09-07 19:16:31
【问题描述】:

我正在尝试(但失败)正确排序我的递归 CTE。我的表由一个父子结构组成,其中一个任务可以在各种不同的级别上与另一个任务相关。

例如,我可以创建一个任务(这是父任务),然后从中创建一个子任务,然后从该子任务创建一个子任务,依此类推..

以下是我包含的一些测试数据。目前它是由Path 按字母顺序排列的。

所以如果我要创建一个任务。它会给我该任务的 TaskID(比如 50)——然后我可以为该主要任务(51、52、53、54、55)创建 5 个子任务。然后我可以将子任务添加到 5 个子任务 (51->56) (53->57) 但是当我想要订单时我需要它返回

所以我需要它的顺序

  • 50
    • 51
      • 56
    • 52
    • 53
      • 57
    • 54
    • 55

测试数据的正确顺序

这是我一直在使用的代码

DECLARE @TaskID NUMERIC(10,0)

SET @TaskID = 38

;WITH cte AS 
(
SELECT 
    t.TaskID
    ,t.ParentID
    ,t.Title
    ,CONVERT(VARCHAR(MAX),'') AS [Nest]
    ,CONVERT(VARCHAR(MAX),'') AS [Path]
    ,t.CreatedDate
FROM 
    tasks.Tasks t
WHERE 
    t.ParentID IS NULL
    AND t.TaskID = @TaskID

UNION ALL

SELECT 
    sub.TaskID
    ,sub.ParentID
    ,sub.Title
    ,cte.[Nest] + CONVERT(VARCHAR(MAX),sub.TaskID) AS [Nest]
    ,cte.[Path] + ',' + CONVERT(VARCHAR(MAX),sub.TaskID) AS [Path]
    ,sub.CreatedDate
FROM 
    tasks.Tasks sub
    INNER JOIN cte ON cte.TaskID = sub.ParentID
)

SELECT 
    TaskID
    ,ParentID
    ,Title
    ,Nest
    ,[Path]
    ,CreatedDate
FROM (
SELECT 
    cte.TaskID
    ,cte.ParentID
    ,cte.Title
    ,NULLIF(LEN(cte.[Path]) - LEN(REPLACE(cte.[Path], ',', '')),0) Nest
    ,CONVERT(VARCHAR(25),@TaskID) + cte.[Path] AS [Path]
    ,cte.CreatedDate
FROM 
    cte
)a
ORDER BY
    a.[Path]

我感觉它会非常明显,但我真的不确定如何进行。我考虑了更多的递归、函数、拆分字符串,但没有成功。

如果我不清楚,请道歉

【问题讨论】:

    标签: sql tsql recursion hierarchy


    【解决方案1】:

    这很简单。 Yuu 不需要使用任何循环或函数。我假设您已经导出了 PATH 值。基于此,我得出了解决方案。

    SELECT C.TASKID, REPLICATE(' ', (LEN([PATH]) - LEN(REPLACE([PATH],',','')) + 2) ) + CONVERT(NVARCHAR(20),C.TASKID) , [小路] 来自 CTE C 按 [路径] 订购

    【讨论】:

    • 如果我错了,请纠正我,但您似乎正在尝试模拟 OP 为显示值发布的树。然而,这棵树只是为了展示预期的顺序。您的代码似乎无助于订购 OP 的数据。
    【解决方案2】:

    最简单的方法是将键填充到固定长度。例如038,007 将在 038,012 之前排序,但是对于最大的任务 ID,填充长度必须是安全的。尽管您可以将 path 修剪以提高可读性,并创建一个额外的填充字段以进行排序。

    一个更安全的版本是这样做,但从 row_numbers 创建一个填充路径。填充大小必须足够大以支持最大数量的子项。

    DECLARE @TaskID NUMERIC(10,0)
    
    SET @TaskID = 38
    
    declare @maxsubchars int = 3 --not more than 999 sub items
    
    ;with cte as
    (
    SELECT 
        t.TaskID
        ,t.ParentID
        ,t.Title
        ,0 AS [Nest]
        ,CONVERT(VARCHAR(MAX),t.taskid) AS [Path]
        ,CONVERT(VARCHAR(MAX),'') OrderPath 
        ,t.CreatedDate  
    FROM 
        tasks.Tasks t
    WHERE 
        t.ParentID IS NULL
        AND t.TaskID = @TaskID
    
    union all
    
    SELECT 
        sub.TaskID
        ,sub.ParentID
        ,sub.Title
        ,cte.Nest + 1
        ,cte.[Path] + ',' + CONVERT(VARCHAR(MAX),sub.TaskID) 
        ,cte.OrderPath + ',' + right(REPLICATE('0', @maxsubchars) + CONVERT(VARCHAR,ROW_NUMBER() over (order by  sub.TaskID)), @maxsubchars) 
        ,sub.CreatedDate
    FROM 
        tasks.Tasks sub
        INNER JOIN cte ON cte.TaskID = sub.ParentID
    )
    select taskid, parentid, title,nullif(nest,0) Nest,Path, createddate from cte order by  OrderPath
    

    您可能会比固定的子项长度更花哨,确定子项的数量并将填充基于所述长度。或者根据兄弟姐妹的数量使用编号的行并以相反的方向遍历并且可能(只是喷出一些未经测试的想法),但使用简单的有序路径可能就足够了。

    【讨论】:

    • 太棒了。我为此奋斗了 3 个小时。我现在可以吻你!哈哈
    【解决方案3】:

    如果最上面的 CTE(如下面的查询)是您的表结构,那么下面的代码可能是解决方案。

    与 CTE 作为
    (
        选择 7112 TASKID,NULL PARENTID UNION ALL
        选择 7120 TASKID ,7112 ParanetID 联合所有
        选择 7139 TASKID ,7112 ParanetID 联合所有
        选择 7150 TASKID ,7112 ParanetID 联合所有
        选择 23682 TASKID ,7112 ParanetID 联合所有
        选择 7100 TASKID ,7112 ParanetID 联合所有
        选择 23691 TASKID ,7112 ParanetID 联合所有
        选择 23696 TASKID ,7112 ParanetID 联合所有
        选择 23700 TASKID ,23696 ParanetID 联合所有
        选择 23694 TASKID ,23691 ParanetID 联合所有
        选择 23689 TASKID ,7120 ParanetID 联合所有
        选择 7148 TASKID ,23696 ParanetID 联合所有
        选择 7126 TASKID ,7120 ParanetID 联合所有
        选择 7094 TASKID ,7120 ParanetID 联合所有
        选择 7098 TASKID ,7094 ParanetID 联合所有
        选择 23687 TASKID ,7094 ParanetID
    
    

    ) ,递归为 ( 选择任务,转换(NVARCHAR(MAX),转换(nvarchar(20),TASKID))[路径] 来自 CTE 其中 PARENTID 为 NULL

    联合所有

    SELECT C.TASKID, CONVERT(NVARCHAR(MAX),convert(nvarchar(20),R.[PATH]) + ',' + convert(nvarchar(20),C.TASKID)) 从递归函数 R 内部连接 ​​CTE C ON R.TASKID = C.PARENTID )

    SELECT C.TASKID, REPLICATE(' ', (LEN([PATH]) - LEN(REPLACE([PATH],',','')) + 2) ) + '.' + 转换(NVARCHAR(20),C.TASKID) 从递归 C 按 [路径] 订购

    尝试在 SSMS 中以文本输出模式执行此查询。这样你就可以看到区别

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-11-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-02
      • 2016-08-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多