订购递归查询的T-SQL - 父/子结构

时间:2015-06-22 10:59:25

标签: sql tsql recursion hierarchy

我正在尝试(并且失败)正确地命令我的递归CTE。我的表由一个父子结构组成,其中一个任务可以在不同的层次上与另一个任务相关联。

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

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

Task Hierarchy

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

所以我要求它的顺序

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

测试数据的正确顺序

True Order

以下是我一直在使用的代码

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]

我有一种感觉会很明显,但我真的不知道如何继续。我想到了更多的递归,函数,拆分字符串没有成功。

如果我不清楚,请道歉

3 个答案:

答案 0 :(得分:1)

最简单的方法是将键填充到固定长度。例如038,007将在038,012之前订购。但填充长度必须对最大的taskid是安全的。虽然您可以保留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

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

答案 1 :(得分:1)

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

WITH CTE AS
(
    SELECT 7112 TASKID ,NULL PARENTID UNION ALL
    SELECT 7120 TASKID ,7112 ParanetID UNION ALL
    SELECT 7139 TASKID ,7112 ParanetID UNION ALL
    SELECT 7150 TASKID ,7112 ParanetID UNION ALL
    SELECT 23682 TASKID ,7112 ParanetID UNION ALL
    SELECT 7100 TASKID ,7112 ParanetID UNION ALL
    SELECT 23691 TASKID ,7112 ParanetID UNION ALL
    SELECT 23696 TASKID ,7112 ParanetID UNION ALL
    SELECT 23700 TASKID ,23696 ParanetID UNION ALL
    SELECT 23694 TASKID ,23691 ParanetID UNION ALL
    SELECT 23689 TASKID ,7120 ParanetID UNION ALL
    SELECT 7148 TASKID ,23696 ParanetID UNION ALL
    SELECT 7126 TASKID ,7120 ParanetID UNION ALL
    SELECT 7094 TASKID ,7120 ParanetID UNION ALL
    SELECT 7098 TASKID ,7094 ParanetID UNION ALL
    SELECT 23687 TASKID ,7094 ParanetID 

) ,RECURSIVECTE AS ( SELECT TASKID, CONVERT(NVARCHAR(MAX),convert(nvarchar(20),TASKID)) [PATH] FROM CTE WHERE PARENTID IS NULL

UNION ALL

SELECT C.TASKID, CONVERT(NVARCHAR(MAX),convert(nvarchar(20),R.[PATH]) + ',' + convert(nvarchar(20),C.TASKID)) FROM RECURSIVECTE R INNER JOIN CTE C ON R.TASKID = C.PARENTID )

SELECT C.TASKID, REPLICATE(' ', (LEN([PATH]) - LEN(REPLACE([PATH],',','')) + 2) ) + '.' + CONVERT(NVARCHAR(20),C.TASKID) FROM RECURSIVECTE C ORDER BY [PATH]

在SSMS中以文本输出模式尝试此查询。这样你就可以看到差异

答案 2 :(得分:0)

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

SELECT C.TASKID,  REPLICATE(' ', (LEN([PATH]) - LEN(REPLACE([PATH],',','')) + 2) ) + CONVERT(NVARCHAR(20),C.TASKID), [PATH]
FROM CTE C
ORDER BY [PATH]