表結構 `Tree(ID, Name, ParentId)`,資料:
ID Name ParentId 1 L1 0 2 L2 1 3 L3 2 4 L4 3
WITH tmpTree AS (
SELECT * FROM Tree WHERE Id = 2 -- 錨點:起始節點
UNION ALL
SELECT p.* FROM tmpTree
INNER JOIN Tree p ON p.ParentId = tmpTree.Id -- 遞迴:找孩子
)
SELECT * FROM tmpTree;
WITH tmpTree AS (
SELECT * FROM Tree WHERE Id = 2
UNION ALL
SELECT p.* FROM tmpTree
INNER JOIN Tree p ON p.Id = tmpTree.ParentId -- 遞迴:找父親
)
SELECT * FROM tmpTree;
-- 表結構調整:新增 Hid HIERARCHYID,移除 ParentId -- 資料範例: -- Hid Id Name -- 0x 1 L1 -- 0x58 2 L2 -- 0x5840 3 L3 -- 0x584040 4 L4 -- 取得所有層級 SELECT *, Hid.GetLevel() AS Lvl FROM Tree2; -- 取得節點 2 的所有下游 DECLARE @Parent HIERARCHYID = (SELECT Hid FROM Tree2 WHERE Id = 2); SELECT *, Hid.GetLevel() AS Lvl FROM Tree2 WHERE Hid.IsDescendantOf(@Parent) = 1; -- 取得節點 3 的所有上游 DECLARE @Child HIERARCHYID = (SELECT Hid FROM Tree2 WHERE Id = 3); SELECT *, Hid.GetLevel() AS Lvl FROM Tree2 WHERE @Child.IsDescendantOf(Hid) = 1;
SELECT * FROM Tree START WITH Id = 2 CONNECT BY PRIOR Id = ParentId;
SELECT * FROM Tree START WITH Id = 2 CONNECT BY Id = PRIOR ParentId;
MySQL 8.0+ 已支援遞迴 CTE,以下為 5.7/8.0 相容寫法。
CREATE FUNCTION `getChildLst`(rootId INT, direction INT) RETURNS VARCHAR(1000) CHARSET utf8
DETERMINISTIC
BEGIN
DECLARE sTemp VARCHAR(5000) DEFAULT '$';
DECLARE sTempChd VARCHAR(1000);
SET sTempChd = CAST(rootId AS CHAR);
IF direction = 1 THEN -- 下游
WHILE sTempChd IS NOT NULL DO
SET sTemp = CONCAT(sTemp, ',', sTempChd);
SELECT GROUP_CONCAT(Id) INTO sTempChd
FROM Tree WHERE FIND_IN_SET(ParentId, sTempChd) > 0;
END WHILE;
ELSEIF direction = 2 THEN -- 上游
WHILE sTempChd IS NOT NULL DO
SET sTemp = CONCAT(sTemp, ',', sTempChd);
SELECT GROUP_CONCAT(ParentId) INTO sTempChd
FROM Tree WHERE FIND_IN_SET(Id, sTempChd) > 0;
END WHILE;
END IF;
RETURN sTemp;
END;
-- 下游:節點 1 的所有子孫 SELECT * FROM Tree WHERE FIND_IN_SET(Id, getChildLst(1, 1)); -- 上游:節點 1 的所有祖先 SELECT * FROM Tree WHERE FIND_IN_SET(Id, getChildLst(1, 2));
-- 下游
WITH RECURSIVE cte AS (
SELECT * FROM Tree WHERE Id = 1
UNION ALL
SELECT t.* FROM cte JOIN Tree t ON t.ParentId = cte.Id
)
SELECT * FROM cte;
-- 上游
WITH RECURSIVE cte AS (
SELECT * FROM Tree WHERE Id = 4
UNION ALL
SELECT t.* FROM cte JOIN Tree t ON t.Id = cte.ParentId
)
SELECT * FROM cte;
| 資料庫 | 機制 | 最大深度 | 效能 | 備註 |
|---|---|---|---|---|
| SQL Server | 遞迴 CTE | 預設 100 層 (`MAXRECURSION` 可調 0=無限) | 優 (索引支援) | 生產首選 |
| Oracle | CONNECT BY | 無硬性限制 | 優 | 語法簡潔 |
| MySQL 8.0+ | 遞迴 CTE | 預設 1000 層 (`cte_max_recursion_depth`) | 優 | 標準 SQL |
| MySQL 5.7 | 函數+FIND_IN_SET | 受限於 `group_concat_max_len` (預設 1024) | 差 (全表掃描、字串操作) | 大樹慎用,建議升級 8.0 |
跨資料庫遞迴查詢:**SQL Server/Oracle/MySQL 8.0+ 皆有原生標準語法 (CTE/CONNECT BY)**,優先使用。MySQL 5.7 僅能靠函數模擬,效能與長度受限,建議規劃升級。`HierarchyID` (SQL Server) 或 `Path` 物化路徑是高頻樹狀查詢的進階優化方向。