公用表表達式(CTE)自 SQL Server 2005 引入,最大亮點是支援自我引用,從而實現遞歸查詢。相比舊版臨時表 + 游標 + 迴圈的笨重寫法,遞歸 CTE 代碼簡潔、可讀性強,專為層級數據(組織架構、物料清單 BOM、分類樹)設計。
WITH cte_name (col1, col2, ...) AS (
-- 定位點成員
SELECT ...
UNION ALL
-- 遞歸成員(引用 cte_name)
SELECT ... FROM base_table JOIN cte_name ON ...
)
SELECT * FROM cte_name;
WITH 可定義多個 CTE,用逗號分隔,僅允許一個 WITH 關鍵字。WITH 中前序 CTE。COMPUTE、ORDER BY(除非配合 TOP)、INTO、OPTION、FOR XML/BROWSE。T0。Ti 輸出 Ti+1,由 UNION ALL 連接。T0。T0 為輸入產生 T1。T0 ∪ T1 ∪ ... ∪ Tn(UNION ALL)。CREATE TABLE dbo.Co_ItemNameSet (
ItemId INT NULL,
ParentItemId INT NULL,
ItemName NCHAR(10) NULL
);
INSERT INTO dbo.Co_ItemNameSet VALUES
(2,0,N'管理費用'),(3,0,N'銷售費用'),(4,0,N'財務費用'),(5,0,N'生產成本'),
(35,5,N'材料'),(36,5,N'人工'),(37,5,N'制造費用'),
(38,35,N'原材料'),(39,35,N'主要材料'),(40,35,N'間輔材料'),
(41,36,N'工資'),(42,36,N'福利'),
(43,2,N'管理費用子項'),(113,43,N'管理費用子項的子項');
需建兩張臨時表、手動維護層級、循環刪除/插入,代碼超 50 行,易出錯、難維護。
DECLARE @RootId INT = 2;
WITH Co_ItemNameSet_CTE (ItemId, ParentItemId, ItemName, [Level]) AS (
-- 定位點:根節點
SELECT ItemId, ParentItemId, ItemName, 1 AS [Level]
FROM dbo.Co_ItemNameSet
WHERE ItemId = @RootId
UNION ALL
-- 遞歸:子節點關聯父節點
SELECT c.ItemId, c.ParentItemId, c.ItemName, ct.[Level] + 1
FROM dbo.Co_ItemNameSet c
INNER JOIN Co_ItemNameSet_CTE ct ON c.ParentItemId = ct.ItemId
)
SELECT * FROM Co_ItemNameSet_CTE;
輸出 5 行:管理費用(L1) → 管理費用子項(L2) → 管理費用子項的子項(L3) 等,完美呈現層級結構。
OPTION (MAXRECURSION 0) 移除限制(謹防無限遞歸)。CREATE INDEX IX_ParentItemId ON Co_ItemNameSet(ParentItemId);Lazy Spool,記憶體壓力較大,超萬層時考慮改用 WHILE + 臨時表 分批處理。遞歸 CTE 是處理層級數據的「瑞士軍刀」:語法標準、邏輯清晰、維護成本低。掌握「定位點 + UNION ALL + 遞歸成員」三部曲,即可輕鬆搞定組織架構、BOM 展開、無限分類等經典場景。