MYSQL中WITH RECURSIVE遞歸查詢的實現(xiàn)
一、前言
從MySQL 8.0開始,可以使用WITH RECURSIVE來創(chuàng)建遞歸公用表表達(dá)式(Common Table Expressions , CTE)。
遞歸查詢在各種應(yīng)用場景中都很常見,例如:
- 組織架構(gòu)查詢: 獲取公司內(nèi)部的組織結(jié)構(gòu)信息,從CEO到普通員工的層級關(guān)系。
- 產(chǎn)品分類查詢: 從頂級分類到子分類的層級查找。
- 目錄結(jié)構(gòu)查詢: 在文件系統(tǒng)中,從根目錄到子目錄的路徑查找。
二、語法
WITH RECURSIVE cte_name (column_list) AS ( SELECT initial_query -- 初始查詢 UNION [ALL] recursive_query -- 遞歸查詢 ) SELECT * FROM cte_name;
其中:
CTE名稱(cte_name)用于標(biāo)識遞歸查詢的臨時結(jié)果集。- 列名列表(
column_list)定義了 CTE 結(jié)果集中包含的列及其名稱。 - 初始查詢(
initial_query)提供遞歸過程的起點(diǎn),即第一次迭代時使用的數(shù)據(jù)。 - 遞歸部分(遞歸子查詢)定義了如何將前一次迭代的結(jié)果作為輸入,計算出下一次迭代的數(shù)據(jù)。
recursive_query:表示遞歸查詢語句,應(yīng)當(dāng)與column_list中的列名對應(yīng)。SELECT * FROM cte_name:表示最終返回的查詢結(jié)果集,可以通過cte_name查詢表中的列名進(jìn)行指定。
1.遞歸查詢的結(jié)構(gòu)
遞歸查詢通常由兩部分構(gòu)成:初始化查詢(非遞歸部分)和遞歸子查詢(遞歸部分)。
- 初始化查詢: 定義遞歸開始時的基礎(chǔ)數(shù)據(jù)集,通常是與遞歸邏輯相關(guān)的最頂層數(shù)據(jù)或邊界條件。
- 遞歸子查詢: 定義如何根據(jù)前一次迭代的結(jié)果生成下一次迭代的數(shù)據(jù)。遞歸子查詢通常包含對自身 CTE 名稱的引用,以遞歸地應(yīng)用相同的操作。
2.連接操作符:
- 遞歸查詢的
初始化查詢和遞歸查詢通常通過UNION或UNION ALL連接起來,形成一個完整的遞歸查詢表達(dá)式。 UNION會去除結(jié)果集中的重復(fù)行,而UNION ALL不會去除重復(fù),根據(jù)實際需求選擇合適的連接操作符。
3.終止條件
- 遞歸查詢必須有一個明確的終止條件,否則會無限循環(huán)下去。終止條件通常隱含在遞歸子查詢的
WHERE子句或其他邏輯中,當(dāng)滿足特定條件時,不再產(chǎn)生新的結(jié)果。
三、示例
通過以下目錄樹示例查詢父節(jié)點(diǎn)、子節(jié)點(diǎn)、及全鏈路等信息。

3.1、創(chuàng)建測試表和數(shù)據(jù)
CREATE TABLE tree_table (
id INT PRIMARY KEY,
name VARCHAR(50),
parent_id INT
);
INSERT INTO tree_table (id, name, parent_id) VALUES
(1, 'A', NULL),
(2, 'B', 1),
(3, 'C', 1),
(4, 'D', 2),
(5, 'E', 3);
(6, 'F', 3);
(7, 'G', 5);
3.2、查詢所有子節(jié)點(diǎn)(以 id=1 為例)
WITH RECURSIVE cte AS (
SELECT id, parent_id, name
FROM tree_table
WHERE id = 1
UNION ALL
SELECT t.id, t.parent_id, t.name
FROM tree_table t
INNER JOIN cte ON t.parent_id = cte.id
)
SELECT * FROM cte;
結(jié)果:

3.3、查詢所有父節(jié)點(diǎn)(以 id=5 為例)
WITH RECURSIVE cte AS (
SELECT id, parent_id, name
FROM tree_table
WHERE id = 5
UNION ALL
SELECT t.id, t.parent_id, t.name
FROM tree_table t
INNER JOIN cte ON t.id = cte.parent_id
)
SELECT * FROM cte;
注意:子節(jié)點(diǎn)與父節(jié)點(diǎn)的區(qū)別在于join的關(guān)聯(lián)條件on的不同。

結(jié)果:

3.4、查詢所有子節(jié)點(diǎn)(向下遞歸)并顯示全鏈路路徑及層級
查詢節(jié)點(diǎn)下的所有子節(jié)點(diǎn),并且可以看到全鏈路及層級,如A->B->C
WITH RECURSIVE cte AS (
SELECT
id,parent_id, name,
name AS path, -- 初始路徑
1 AS level -- 起始層級
FROM tree_table
WHERE id = 1 -- 查詢ID=1的所有子節(jié)點(diǎn)
UNION ALL
SELECT
t.id,t.parent_id,t.name,
CONCAT(cte.path, '->', t.name), -- 路徑拼接
cte.level + 1 -- 層級遞增
FROM tree_table t
INNER JOIN cte ON t.parent_id = cte.id
)
SELECT * FROM cte;
結(jié)果如下:

3.5、查詢所有父節(jié)點(diǎn)(向上遞歸)并顯示全鏈路路徑及層級
查詢節(jié)點(diǎn)的所有父節(jié)點(diǎn),并且可以看到全鏈路及層級,如A->B->C
WITH RECURSIVE cte AS (
SELECT
id, parent_id, name,
CAST(name AS CHAR(255)) AS path, -- 路徑初始化
1 AS level -- 起始層級
FROM tree_table
WHERE id = 6 -- 查詢ID=6的所有父節(jié)點(diǎn)
UNION ALL
SELECT
t.id, t.parent_id, t.name,
CONCAT(t.name, '->', cte.path), -- 向前拼接路徑
cte.level + 1 -- 層級遞增
FROM tree_table t
INNER JOIN cte ON t.id = cte.parent_id
)
SELECT * FROM cte
ORDER BY level ASC; -- 按層級升序排列
結(jié)果如下:

3.6、高級功能:雙向遞歸查詢(同時獲取祖先和后代)
-- 獲取ID=5的所有祖先和后代
WITH RECURSIVE
down AS ( /* 向下遞歸獲取后代 */
SELECT id, parent_id, name, name AS path, 1 AS level
FROM tree_table WHERE id = 5
UNION ALL
SELECT t.id, t.parent_id, t.name,
CONCAT(down.path, '->', t.name),
down.level + 1
FROM tree_table t
INNER JOIN down ON t.parent_id = down.id
),
up AS ( /* 向上遞歸獲取祖先 */
SELECT id, parent_id, name, name AS path, 1 AS level
FROM tree_table WHERE id = 5
UNION ALL
SELECT t.id, t.parent_id, t.name,
CONCAT(t.name, '->', up.path),
up.level + 1
FROM tree_table t
INNER JOIN up ON t.id = up.parent_id
)
SELECT * FROM up WHERE id != 5 -- 排除重復(fù)的當(dāng)前節(jié)點(diǎn)
UNION ALL
SELECT * FROM down;
結(jié)果如下:

上圖示例中同時查詢了父節(jié)點(diǎn)與子節(jié)點(diǎn),可能從返回結(jié)果中不能區(qū)別出哪些是父節(jié)點(diǎn)哪些是子節(jié)點(diǎn)。這里改造一下:
-- 獲取ID=5的所有祖先和后代
WITH RECURSIVE
down AS ( /* 向下遞歸獲取后代 */
SELECT id, parent_id, name, name AS path, 1 AS level,'down' as type
FROM tree_table WHERE id = 5
UNION ALL
SELECT t.id, t.parent_id, t.name,
CONCAT(down.path, '->', t.name),
down.level + 1,
'down' as type
FROM tree_table t
INNER JOIN down ON t.parent_id = down.id
),
up AS ( /* 向上遞歸獲取祖先 */
SELECT id, parent_id, name, name AS path, 1 AS level,'up' as type
FROM tree_table WHERE id = 5
UNION ALL
SELECT t.id, t.parent_id, t.name,
CONCAT(t.name, '->', up.path),
up.level + 1,
'up' as type
FROM tree_table t
INNER JOIN up ON t.id = up.parent_id
)
SELECT * FROM up WHERE id != 5 -- 排除重復(fù)的當(dāng)前節(jié)點(diǎn)
UNION ALL
SELECT * FROM down;
添加type字段區(qū)分父節(jié)點(diǎn)、子節(jié)點(diǎn)。

四、擴(kuò)展
4.1、注意事項:
- 遞歸深度限制: MySQL 默認(rèn)遞歸深度為 1000,超限會報錯??赏ㄟ^設(shè)置會話變量調(diào)整:
SET SESSION cte_max_recursion_depth = 10000; -- 修改遞歸深度
避免死循環(huán): 確保數(shù)據(jù)中沒有循環(huán)引用(如 A→B→A)。
性能: 大數(shù)據(jù)量時確保 parent_id 字段有索引:
CREATE INDEX idx_parent_id ON tree_table(parent_id);
- 路徑長度限制:
CAST(name AS CHAR(255)) -- 根據(jù)實際需要調(diào)整長度
4.2、避免死循環(huán)
避免死循環(huán): 確保數(shù)據(jù)中沒有循環(huán)引用(如 A→B→A)
如果遇到循環(huán)引用會導(dǎo)致遞歸查詢無限循環(huán)查詢,從而引發(fā)數(shù)據(jù)庫異常報錯。
解決方法:
- 方法一:避免死循環(huán),確保數(shù)據(jù)中沒有循環(huán)引用(如
A→B→A) - 方法二:限制遞歸層級,查詢時設(shè)置遞歸層級,如小于10級
4.3、結(jié)果排序技巧
- 按層級排序:
ORDER BY level ASC - 按路徑排序:
ORDER BY full_path ASC - 按樹形結(jié)構(gòu)排序:
ORDER BY LENGTH(full_path) - LENGTH(REPLACE(full_path, '->', '')), full_path
到此這篇關(guān)于MYSQL中WITH RECURSIVE遞歸查詢的實現(xiàn)的文章就介紹到這了,更多相關(guān)MYSQL WITH RECURSIVE遞歸查詢內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mysql 數(shù)據(jù)庫鏈接狀態(tài)確認(rèn)實驗(推薦)
這篇文章主要介紹了mysql 數(shù)據(jù)庫鏈接狀態(tài)確認(rèn)實驗,通過本文我選擇 了三種方案給大家詳細(xì)講解,結(jié)合實例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09
mysql5.5 master-slave(Replication)主從配置
在主機(jī)master中對test數(shù)據(jù)庫進(jìn)行sql操作,再查看從機(jī)test數(shù)據(jù)庫是否產(chǎn)生同步。2011-07-07
MySQL實現(xiàn)定時自動備份的流程步驟(Windows環(huán)境)
這篇文章主要介紹了MySQL實現(xiàn)定時自動備份的流程步驟(Windows環(huán)境),文中通過圖文結(jié)合的方式介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-12-12
MySQL數(shù)據(jù)庫如何開啟遠(yuǎn)程連接(多備份)
多備份服務(wù)器在備份你的數(shù)據(jù)庫時,必須能夠遠(yuǎn)程連接上你的數(shù)據(jù)庫。但是一般來說mysql安裝時都是關(guān)閉遠(yuǎn)程連接的,因此,需要你開通mysql數(shù)據(jù)庫的遠(yuǎn)程訪問權(quán)限。那么如何開啟呢2015-01-01
如何使用MySQL?Explain?分析?SQL?執(zhí)行計劃
MySQL?提供的?EXPLAIN?工具能夠幫助我們深入了解查詢語句的執(zhí)行過程、索引使用情況以及潛在的性能瓶頸,本文將詳細(xì)介紹如何使用?EXPLAIN?分析?SQL?執(zhí)行計劃,并探討其中各個重要字段的含義以及優(yōu)化建議,感興趣的朋友一起看看吧2025-04-04

