資料表 t_tree 儲存三層行政區域:省 → 市 → 區。
CREATE TABLE t_tree (id INT PRIMARY KEY, node_name NVARCHAR(50), parent_id INT);
INSERT INTO t_tree VALUES
(1, N'遼寧省', 0),
(2, N'瀋陽市', 1), (3, N'大連市', 1),
(4, N'大東區', 2), (5, N'瀋河區', 2), (6, N'鐵西區', 2);
需求:**查詢「遼寧省」及其所有下層市、區 (含自身)**。
WHERE node_name = N'遼寧省'。id 作為 parent_id 查詢下一層。@@ROWCOUNT = 0)。UNION ALL 逐層合併。
圖 1:遞迴流程示意圖
WITH cte_name (column_list) AS (
-- 錨點成員
SELECT ... FROM ... WHERE root_condition
UNION ALL
-- 遞迴成員 (引用 cte_name)
SELECT ... FROM source_table JOIN cte_name ON join_condition
)
SELECT * FROM cte_name;
WITH district AS (
-- 1. 錨點:取得根節點
SELECT id, node_name, parent_id
FROM t_tree
WHERE node_name = N'遼寧省'
UNION ALL
-- 2. 遞迴:依 parent_id 向下展開
SELECT t.id, t.node_name, t.parent_id
FROM t_tree t
INNER JOIN district d ON t.parent_id = d.id
)
SELECT * FROM district
ORDER BY parent_id, id;
| id | node_name | parent_id | 層級 |
|---|---|---|---|
| 1 | 遼寧省 | 0 | 0 (根) |
| 2 | 瀋陽市 | 1 | 1 |
| 3 | 大連市 | 1 | 1 |
| 4 | 大東區 | 2 | 2 |
| 5 | 瀋河區 | 2 | 2 |
| 6 | 鐵西區 | 2 | 2 |

圖 2:完整遍歷結果
WITH district AS (
SELECT id, node_name, parent_id FROM t_tree WHERE node_name = N'遼寧省'
UNION ALL
SELECT t.id, t.node_name, t.parent_id FROM t_tree t INNER JOIN district d ON t.parent_id = d.id
),
district_non_leaf AS (
SELECT d.* FROM district d
WHERE d.id IN (SELECT parent_id FROM district)
)
SELECT * FROM district_non_leaf;

圖 3:非葉子節點 (遼寧省、瀋陽市)
| 規則編號 | 限制內容 | 違反後果 |
|---|---|---|
| 1 | 至少一個錨點成員、一個遞迴成員;錨點必須在遞迴之前 | 語法錯誤 |
| 2 | 錨點之間、最後錨點與第一遞迴之間:僅允許 UNION ALL | 語法錯誤 (UNION/INTERSECT/EXCEPT 僅限錨點內部) |
| 3 | 錨點與遞迴欄位數、資料型別**必須完全一致** | 型別轉換錯誤/結果錯誤 |
| 4 | 遞迴成員 FROM 子句**只能引用一次** CTE 名稱 | 語法錯誤 (防止多重遞迴導致指數爆炸) |
| 5 | 遞迴成員**禁止**:DISTINCT、GROUP BY、HAVING、標量彙總、TOP、LEFT/RIGHT/Outer JOIN (允許 INNER JOIN)、子查詢、提示 | 語法錯誤 |
| 6 | 回傳欄位**皆可為 NULL** (不論來源欄位可空性) | 需注意後續處理 NULL |
| 7 | **無限迴圈防護**:OPTION (MAXRECURSION n) (0=無限制, 預設 100, 最大 32767) | 預設 100 層自動終止,深層樹需調大 |
| 8 | 包含遞迴 CTE 的視圖**不可更新** | 限制視圖用途 |
| 9 | 游標類型限制:僅支援 Fast Forward / Static | 其他類型自動轉 Static |
| 10 | 可引用遠端伺服器表 (Linked Server),每個遠端表建立偽脫機 | 跨伺服器遞迴可行但效能需評估 |
parent_id **必須建立索引** (非叢集即可),避免每層掃描全表。id, parent_id, node_name),減少記憶體/TempDB 壓力。OPTION (MAXRECURSION 1000)),防止錯誤資料導致無限迴圈佔用資源。CTE 遞迴查詢以 **聲明式 SQL** 优雅解決樹狀結構遍歷問題,取代游標/暫存表/迴圈等程序式寫法。掌握「錨點→遞迴→UNION ALL→終止」四步曲,並嚴守 10 大限制規範,即可安全高效處理組織架構、產品分類、BOM 物料清單、帳號推薦關係等層級資料。搭配 MAXRECURSION 守門與索引優化,讓遞迴查詢在生產環境穩定可控。