2015-07-20 137 views
2

我有2個表,第一個包含文檔的數據,第二個包含目錄。SQL Server CTE - 遞歸

表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 

根未顯示,文檔由Order其目錄內排序,結果由全路徑的名稱排序最低的深度排序。

+4

顯示你查詢,你試過了什麼? – Fabio

+0

請注意'order'列名是一個[保留字](https://msdn.microsoft.com/en-us/library/ms189822.aspx)注意語法 – xQbert

+0

我相信** Doc4 **的輸出是錯誤的:'Dir1/Dir2/Doc4'。它應該是邏輯上的「Dir1/Doc4」。 – FutbolFan

回答

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,你可以很容易地創建一個路徑如上圖所示:

注意:我相信你的輸出是錯誤的文檔4Dir1/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

+0

@Triber這個答案有什麼問題嗎? – FutbolFan

+0

抱歉,遲到的迴應,我剛剛經歷了一些較老的問題,我錯過了這一個。如果我記得好,第二個答案是更長的時間,但對我來說似乎更清楚,但都起作用。 – Triber