2010-07-01 153 views
6

我試圖執行我認爲使用CTE的困難遞歸是SQL Server 2008.我似乎無法將我的頭圍繞在這一個。SQL Server 2008 CTE遞歸

在下面的例子中,你可以假定固定的深度爲3 ......沒有比這更低的了。在現實生活中,深度「更深」但仍然固定。在這個例子中,我試圖簡化它。

我的輸入數據如下所示。

ID  PARENT_ID  NAME   DEPTH 
------------------------------------------ 
1  NULL   A    1 
2  1    B    2 
3  2    C    3 
4  1    D    2 

我的CTE的輸出應該是下表。

LEVEL1_ID LEVEL2_ID LEVEL3_ID LEVEL1_NAME LEVEL2_NAME LEVEL3_NAME 
-------------------------------------------------------------------------------- 
1   NULL   NULL   A    NULL   NULL 
1   2   NULL   A    B    NULL 
1   2   3   A    B    C 
1   4   NULL   A    D    NULL 

如果我可以在輸出中獲得ID列,我當然可以映射到查找表中的名稱。

我願意接受其他方式來完成這項工作,包括使用SSIS。

+0

我不明白你是看你的輸出端施加什麼邏輯。我認爲最後一行應該是'/ B/D',因爲'D'的父ID是2,即'B' – IsmailS 2010-07-01 15:25:04

+0

剛剛更新了這個問題。抱歉。 – thomas 2010-07-01 15:26:17

+0

再次相同。在#4行中,爲什麼當它的父ID是2時,需要Level1_ID爲'1'? – IsmailS 2010-07-01 15:29:58

回答

9

不是真的那麼難做到:

;WITH cte AS 
(
    SELECT CAST('/' + Name AS VARCHAR(50)) as 'CteName', ID 
    FROM dbo.YourTable 
    WHERE parent_id IS NULL 

    UNION ALL 

    SELECT CAST(cte.CteName + '/' + Name AS VARCHAR(50)), t.ID 
    FROM dbo.YourTable t 
    INNER JOIN cte ON t.parent_id = cte.id 
) 
SELECT cteName FROM cte 
ORDER BY ID 

給我的輸出:

/A 
/A/B 
/A/B/C 
/A/D 

作爲一個側面說明:「深度」可以由CTE和容易計算您不一定需要將其存儲在您的表格中(請參閱我已添加的Level列):

;WITH cte AS 
(
    SELECT 
     CAST('/' + Name AS VARCHAR(50)) as 'CteName', ID, 
     1 AS 'Level' 
    FROM dbo.YourTable 
    WHERE parent_id IS NULL 

    UNION ALL 

    SELECT 
     CAST(cte.CteName + '/' + Name AS VARCHAR(50)), t.ID, 
     cte.Level + 1 AS 'Level' 
    FROM dbo.YourTable t 
    INNER JOIN cte ON t.parent_id = cte.id 
) 
SELECT cteName FROM cte 
ORDER BY Level, ID 
+1

這太棒了。謝謝!我稍微改變了這個問題,但這很好。我當然可以使用這個例子。 – thomas 2010-07-01 15:29:46

+0

大marc_s!這麼快!!!! – IsmailS 2010-07-01 15:32:03

+0

有了這段代碼,你就沒有多次加入關卡的問題了?例如,在遞歸運行的第一次運行期間,您將加入級別爲2的級別1,在第二次運行期間,您將再次加入級別1和級別2,級別2加上級別3,等等。重複行被UNION操作符「刪除」,但它會做很多重複的連接。 – munissor 2010-07-01 15:39:10

0

我不記得你可以在cte中做一個子查詢。

我沒有SQL Server的副本,在這裏,但你可以用這個代碼嘗試:

WITH cte(id, path, level) 
AS 
(
    SELECT id, '/' + name, level 
    FROM yourtable 
    WHERE level = 1 

    UNION ALL 

    SELECT y.id, c.name + '/' + y.name, y.level 
    FROM yourtable y INNER JOIN 
    cte c ON c.id = y.parent_id 
    WHERE level = (SELECT max(level)+1 from cte) 
) 
SELECT path from cte 
+0

我只收到大量錯誤:消息207,級別16,狀態1,行6 無效的列名稱「級別」。 消息207,級別16,狀態1,行4 無效的列名稱「級別」。 消息253,級別16,狀態1,行1 公用表表達式'cte'的遞歸成員具有多個遞歸引用。 – 2010-07-01 15:43:10

+1

好吧,你不能使用子查詢;) – munissor 2010-07-01 16:08:08

-1
;WITH Vals AS (
     SELECT CASE DEPTH WHEN 1 THEN ID ELSE NULL END 'LEVEL1_ID ', 
       CASE DEPTH WHEN 2 THEN ID ELSE NULL END 'LEVEL2_ID ', 
       CASE DEPTH WHEN 3 THEN ID ELSE NULL END 'LEVEL3_ID ', 
       CASE DEPTH WHEN 1 THEN NAME ELSE NULL END 'LEVEL1_NAME', 
       CASE DEPTH WHEN 2 THEN NAME ELSE NULL END 'LEVEL2_NAME', 
       CASE DEPTH WHEN 3 THEN NAME ELSE NULL END 'LEVEL3_NAME', 
       ID 'PRMID'     
     FROM #Table1 
     WHERE parentId IS NULL 
     UNION ALL 
     SELECT CASE DEPTH WHEN 1 THEN ID ELSE LEVEL1_ID END 'LEVEL1_ID ', 
       CASE DEPTH WHEN 2 THEN ID ELSE LEVEL2_ID END 'LEVEL2_ID ', 
       CASE DEPTH WHEN 3 THEN ID ELSE LEVEL3_ID END 'LEVEL3_ID ', 
       CASE DEPTH WHEN 1 THEN NAME ELSE LEVEL1_NAME END 'LEVEL1_NAME', 
       CASE DEPTH WHEN 2 THEN NAME ELSE LEVEL2_NAME END 'LEVEL2_NAME', 
       CASE DEPTH WHEN 3 THEN NAME ELSE LEVEL3_NAME END 'LEVEL3_NAME', 
       ID 'PRMID'        
     FROM #Table1 inner join Vals on #Table1.parentId=PRMID 

) 

SELECT * from Vals