SQL Server get path with recursive CTE

纵饮孤独 提交于 2021-02-10 06:59:16

问题


I want to get the path for each department with this format 1.1, 1.2 and so on. This is my department table :

id  name    parentId
--------------------
1   Dep 1   0
2   Dep 2   1
3   Dep 3   0
4   Dep 4   1
5   Dep 5   4
6   Dep 6   2

This is my recursive CTE that give me the parents and children in a flat table starting from a root department.

WITH recursiveCte (parentId, id, name, Level)
AS
(
    -- Anchor member definition
    SELECT 
        d.parentId, d.id, d.name, 
        0 AS Level
    FROM
        Department AS d
    WHERE 
        parentId = 0 

    UNION ALL

    -- Recursive member definition
    SELECT 
        d.parentId, d.id, d.name,
        Level + 1
    FROM 
        Department AS d
    INNER JOIN 
        recursiveCte AS r ON d.parentId = r.id
)
-- Statement that executes the CTE
SELECT parentId,id, name, Level
FROM recursiveCte 
ORDER BY id

Current results:

parentId    id  name    Level
-------------------------------
0           1   Dep 1   0
1           2   Dep 2   1
0           3   Dep 3   0
1           4   Dep 4   1
4           5   Dep 5   2
2           6   Dep 6   2

Desired results:

parentId    id  name    Level   Path
--------------------------------------
0           1   Dep 1   0       1  
1           2   Dep 2   1       1.1 
2           6   Dep 6   2       1.1.1
1           4   Dep 4   1       1.2
4           5   Dep 5   2       1.2.1 
0           3   Dep 3   0       2 

Thanks.


回答1:


Here is a working solution. It is difficult to describe in words why this works, so I recommend taking apart the query to see how it works yourself. Basically, we recursively build the path string you want to see, using ROW_NUMBER to keep track to which particular parent each new path addition belongs.

recursiveCte (parentId, id, name, Level, Path, FullPath) AS (
    SELECT d.parentId, d.id, d.name, 0 AS Level,
        CAST(ROW_NUMBER() OVER (ORDER BY d.id) AS nvarchar(max)),
        RIGHT('000' + CAST(ROW_NUMBER() OVER (ORDER BY d.id) AS nvarchar(max)), 3)
    FROM Department AS d
    WHERE parentId = 0 

    UNION ALL

    SELECT d.parentId, d.id, d.name, r.Level + 1,
        r.Path + '.' +
        CAST(ROW_NUMBER() OVER (PARTITION BY r.Level ORDER BY d.id) AS nvarchar(max)),
        r.FullPath + '.' + RIGHT('000' + CAST(ROW_NUMBER() OVER
            (PARTITION BY r.Level ORDER BY d.id) AS nvarchar(max)), 3)
    FROM Department AS d
    INNER JOIN recursiveCte AS r
        ON d.parentId = r.id
)

SELECT parentId, id, name, Level, Path, FullPath
FROM recursiveCte
ORDER BY FullPath;

Demo

Edit:

I slightly edited my original answer so that it now sorts the path string using a fixed-width version, i.e. every number has a fixed width of 3 digits. This means that 001 will always sort before 010, which is the behavior we want.



来源:https://stackoverflow.com/questions/50893656/sql-server-get-path-with-recursive-cte

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!