CREATE TABLE Menu (
ID INT PRIMARY KEY,
Name NVARCHAR(50),
ParentID INT NULL REFERENCES Menu(ID)
);
INSERT INTO Menu VALUES
(1, N'新聞', NULL),
(2, N'房產', NULL),
(3, N'科技新聞', 1),
(4, N'社會新聞', 1),
(5, N'IT新聞', 3),
(6, N'航天新聞', 3);
利用變量 @Path 存儲「已訪問節點 ID 列表」(逗號分隔),循環擴展直到無新節點。
DECLARE @Path VARCHAR(1000) = ','; -- 頭尾逗號便於 CHARINDEX 匹配
-- 1. 初始化:放入根節點
SELECT @Path = @Path + CAST(ID AS VARCHAR) + ','
FROM Menu WHERE ID = 1; -- 假設查詢 ID=1 的子孫樹
-- 2. 迭代擴展
WHILE @@ROWCOUNT > 0
BEGIN
-- 找出:父節點在 @Path 中,且自身不在 @Path 中的節點
SELECT @Path = @Path + CAST(ID AS VARCHAR) + ','
FROM Menu
WHERE CHARINDEX(',' + CAST(ParentID AS VARCHAR) + ',', @Path) > 0 -- 父在路徑中
AND CHARINDEX(',' + CAST(ID AS VARCHAR) + ',', @Path) = 0; -- 自身不在路徑中
END;
-- 3. 輸出結果
SELECT * FROM Menu
WHERE CHARINDEX(',' + CAST(ID AS VARCHAR) + ',', @Path) > 0;
@Path 格式:,1,3,5,6, —— 頭尾逗號確保 CHARINDEX(',3,', @Path) 精確匹配單個 ID,避免 13 誤匹配 3。@@ROWCOUNT > 0 判斷本輪是否有新節點加入,無則結束。WITH cte AS (
SELECT ID, Name, ParentID, 0 AS Level
FROM Menu WHERE ID = 1 -- 根節點
UNION ALL
SELECT m.ID, m.Name, m.ParentID, c.Level + 1
FROM Menu m
INNER JOIN cte c ON m.ParentID = c.ID
)
SELECT * FROM cte
OPTION (MAXRECURSION 100); -- 防無限遞歸,默認 100 層
優勢:標準 SQL、可攜性強、自動處理層級、可輸出層級欄位、優化器支持並行。
適用性:深度 ≤ 100 層(可用 MAXRECURSION 0 解除限制,慎用)。
| 方案 | 適用版本 | 深度限制 | 性能 | 可讀性 |
|---|---|---|---|---|
| WHILE + 字符串 | 2000+ | 無(受變量長度限制) | 大樹慢(字符串拼接、全表掃描每輪) | 低 |
| CTE 遞歸 | 2005+ | 默認 100,可調 0 | 優(索引求值、流水線執行) | 高 |
| PathEnumeration (物化路徑) | 所有 | 無 | 極快(單次索引查找) | 中(需維護路徑列) |
/1/3/5/ 類路徑列,索引前綴查詢)或嵌套集合模型(左/右值編碼)。