SQL Server CTE - 递归

问题描述 投票:2回答:2

我有2个表,第一个包含文档数据,第二个包含目录。

表格1

DocID  DirID  Name  Order
-----  -----  ----  -----
1      4      Doc1  2
2      1      Doc2  1
3      5      Doc3  1
4      3      Doc4  1
5      4      Doc5  1

表2

DirID  ParentID  Name
-----  --------  ----
1      NULL      root
2      1         Dir1
3      2         Dir2
4      1         Dir3
5      3         Dir4

结构体

root
-Dir1
 -Dir2
  -Dir4
   -Doc3 
  -Doc4 
-Dir3
 -Doc5 
 -Doc1 
-Doc2 

我正在尝试在T-SQL中创建CTE,这将生成此结果,但我无法弄清楚如何做到这一点。有人可以提出解决方案吗?

Doc2
Dir3/Doc5
Dir3/Doc1
Dir1/Dir2/Doc4
Dir1/Dir2/Dir4/Doc3

未显示Root,文档在其目录中按Order排序,结果按照完整路径名称排序的最低深度排序。

sql sql-server common-table-expression
2个回答
2
投票

使用CTE生成文件夹层次结构 然后将文档加入文件夹 并使用创建的Path为此目的订购结果

DECLARE @Separator AS VARCHAR(1) = '\\'
--Generating folder hierarchy
;WITH Info AS
(
    SELECT f.DirID
    , f.ParentID
    , f.Name
    , CAST(f.Name AS VARCHAR(255)) AS PathValue
    FROM Folders f
    WHERE f.ParentID IS NULL

    UNION ALL

    SELECT f.DirID
    , f.ParentID
    , f.Name
    , CAST(i.PathValue + @Separator + f.Name AS VARCHAR(255))
    FROM Folders f
    INNER JOIN Info i ON f.ParentID = i.DirID
)

-- Join documents to the folder hirarchy
SELECT i.ParentID
, i.DirID
, i.Name
, d.DocID
, d.Name
, i.PathValue + @Separator + CAST(d.OrderNum AS VARCHAR(255)) + '.' + d.Name AS OrderPath
, i.PathValue + @Separator + d.Name AS DocumentPath
FROM Info i
INNER JOIN Documents d ON d.DirID = i.DirID

UNION ALL

-- Adding NULL row for folder, will show folder in the result even no documents
-- This can be removed if you want show only folders which containing documents
SELECT i.ParentID
, i.DirID
, i.Name
, NULL
, NULL
, i.PathValue AS OrderPath
, i.PathValue AS DocumentPath
FROM Info i
ORDER BY OrderPath

SQL Fiddle


2
投票

使用Recursive CTE,您可以轻松创建如上所示的路径:

注意:我相信你的输出对于Doc4:Dir1/Dir2/Doc4是错误的。每个逻辑应该是Dir1/Doc4

;WITH q1
AS (
    SELECT a.DocID
        ,b.*
        ,a.NAME AS rootname
    FROM tableb b
    LEFT JOIN tablea a ON b.DirID = a.DirID
    )
,q2
AS (
    -- anchor 
    SELECT DocID
        ,DirID
        ,q1.rootname
        ,ParentID
        ,CAST((q1.NAME) AS VARCHAR(1000)) [Path]
    FROM q1
    WHERE ParentId IS NULL

    UNION ALL

    --recursive member 
    SELECT t.DocID
        ,t.DirID
        ,t.rootname
        ,t.ParentID
        ,CAST((a.path + '/' + t.NAME) AS VARCHAR(1000)) [Path]
    FROM q1 AS t
    INNER JOIN q2 AS a ON t.ParentId = a.DirID
    )
SELECT replace([Path] + '/' + q2.rootname, 'root/', '') AS FinalPath
FROM q2
WHERE q2.rootname IS NOT NULL
ORDER BY FinalPath DESC

SQL Fiddle Demo

© www.soinside.com 2019 - 2024. All rights reserved.