目录
- [一、什么是 WITH RECURSIVE](#一、什么是 WITH RECURSIVE)
- [二、MySQL 从哪个版本开始支持?](#二、MySQL 从哪个版本开始支持?)
- 三、它解决了什么问题
- [四、WITH RECURSIVE 语法结构](#四、WITH RECURSIVE 语法结构)
- 五、递归执行过程
- [六、WITH RECURSIVE 的组成部分](#六、WITH RECURSIVE 的组成部分)
-
- [1. Anchor(锚点)](#1. Anchor(锚点))
- [2. Recursive(递归部分)](#2. Recursive(递归部分))
- [3. UNION ALL](#3. UNION ALL)
- 七、第一个实战:生成数字序列
- 八、生成日期序列
- 九、树形结构实战
- 十、递归向下查询(查询所有子节点)
- 十一、增加层级显示
- 十二、生成完整路径
- [十三、WITH RECURSIVE 能向上查吗?](#十三、WITH RECURSIVE 能向上查吗?)
- 十四、向上递归查询祖先节点
- 十五、向上生成完整路径
- 十六、避免死循环
- 十七、递归层数限制
- [十八、WITH RECURSIVE 使用规则总结](#十八、WITH RECURSIVE 使用规则总结)
- 十九、企业开发中的典型应用
- 二十、面试高频问题
-
-
- [Q1:WITH RECURSIVE 从哪个版本开始支持?](#Q1:WITH RECURSIVE 从哪个版本开始支持?)
- [Q2:WITH 和 WITH RECURSIVE 区别?](#Q2:WITH 和 WITH RECURSIVE 区别?)
- Q3:能否查询父节点?
- [Q4:为什么推荐 WITH RECURSIVE?](#Q4:为什么推荐 WITH RECURSIVE?)
-
- 总结
一、什么是 WITH RECURSIVE
WITH RECURSIVE 是 MySQL 提供的一种递归公共表表达式(Recursive Common Table Expression,Recursive CTE)。
简单理解:
它允许 SQL 自己调用自己,实现递归查询。
类似于 Java 中:
java
public void test(Node node){
test(node.getChild());
}
SQL 以前无法直接递归,只能:
- 多次自关联(JOIN)
- 应用层循环查询
- 存储过程递归
而 WITH RECURSIVE 出现后,可以直接在 SQL 中完成树形结构遍历。
二、MySQL 从哪个版本开始支持?
MySQL 从:
MySQL 8.0
开始正式支持:
- CTE(公共表表达式)
- Recursive CTE(递归公共表表达式)
即:
sql
WITH ...
和
sql
WITH RECURSIVE ...
都是 MySQL 8.0 新增的特性。
MySQL 5.7 及以前:
❌ 不支持
三、它解决了什么问题
开发中经常出现树形结构:
组织架构
text
董事长
├── 总经理
│ ├── 技术部
│ └── 财务部
└── 人事部
菜单系统
text
系统管理
├── 用户管理
├── 角色管理
└── 权限管理
行政区划
text
中国
├── 北京
├── 上海
└── 广东
├── 深圳
└── 广州
评论回复
text
评论1
├── 回复1
│ └── 回复2
└── 回复3
以前查询:
查询所有子节点
需要:
sql
select * from dept where parent_id=1;
select * from dept where parent_id in(...);
select * from dept where parent_id in(...);
不断循环。
现在:
sql
WITH RECURSIVE
一条 SQL 搞定。
四、WITH RECURSIVE 语法结构
标准语法:
sql
WITH RECURSIVE cte_name AS (
-- 初始查询(锚点查询)
SELECT ...
UNION ALL
-- 递归查询
SELECT ...
FROM table t
JOIN cte_name c
ON ...
)
SELECT * FROM cte_name;
五、递归执行过程
例如:
sql
WITH RECURSIVE nums AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1
FROM nums
WHERE n < 5
)
SELECT * FROM nums;
执行步骤:
第一步
执行锚点查询
sql
SELECT 1
结果:
text
1
第二步
带入递归部分
sql
1 + 1
得到:
text
2
第三步
继续递归
text
3
4
5
结果:
text
1
2
3
4
5
六、WITH RECURSIVE 的组成部分
必须包含两部分:
1. Anchor(锚点)
递归起点
sql
SELECT 1
2. Recursive(递归部分)
不断调用自身
sql
SELECT n+1
FROM nums
WHERE n<5
3. UNION ALL
连接两部分
sql
Anchor
UNION ALL
Recursive
七、第一个实战:生成数字序列
生成1~10
sql
WITH RECURSIVE nums AS (
SELECT 1 AS num
UNION ALL
SELECT num + 1
FROM nums
WHERE num < 10
)
SELECT * FROM nums;
结果:
text
1
2
3
4
5
6
7
8
9
10
八、生成日期序列
生成最近7天
sql
WITH RECURSIVE dates AS (
SELECT CURDATE() AS dt
UNION ALL
SELECT DATE_SUB(dt,INTERVAL 1 DAY)
FROM dates
WHERE dt > CURDATE()-INTERVAL 6 DAY
)
SELECT * FROM dates;
结果:
text
2026-06-15
2026-06-14
2026-06-13
...
九、树形结构实战
部门表
sql
CREATE TABLE dept(
id INT PRIMARY KEY,
dept_name VARCHAR(50),
parent_id INT
);
数据:
text
1 总公司 NULL
2 技术中心 1
3 财务中心 1
4 开发部 2
5 测试部 2
6 Java组 4
7 前端组 4
数据结构
text
总公司(1)
├── 技术中心(2)
│ ├── 开发部(4)
│ │ ├── Java组(6)
│ │ └── 前端组(7)
│ └── 测试部(5)
└── 财务中心(3)
十、递归向下查询(查询所有子节点)
查询部门1下面所有节点
sql
WITH RECURSIVE dept_tree AS (
SELECT
id,
dept_name,
parent_id,
1 level
FROM dept
WHERE id = 1
UNION ALL
SELECT
d.id,
d.dept_name,
d.parent_id,
dt.level + 1
FROM dept d
JOIN dept_tree dt
ON d.parent_id = dt.id
)
SELECT *
FROM dept_tree;
结果:
text
1 总公司
2 技术中心
3 财务中心
4 开发部
5 测试部
6 Java组
7 前端组
十一、增加层级显示
sql
WITH RECURSIVE dept_tree AS (
SELECT
id,
dept_name,
parent_id,
1 level
FROM dept
WHERE id=1
UNION ALL
SELECT
d.id,
d.dept_name,
d.parent_id,
dt.level+1
FROM dept d
JOIN dept_tree dt
ON d.parent_id=dt.id
)
SELECT
id,
dept_name,
level
FROM dept_tree;
结果:
text
id dept_name level
1 总公司 1
2 技术中心 2
3 财务中心 2
4 开发部 3
5 测试部 3
6 Java组 4
7 前端组 4
十二、生成完整路径
很多权限系统都这样做。
例如:
text
总公司/技术中心/开发部/Java组
SQL:
sql
WITH RECURSIVE dept_tree AS (
SELECT
id,
dept_name,
parent_id,
dept_name AS path
FROM dept
WHERE id=1
UNION ALL
SELECT
d.id,
d.dept_name,
d.parent_id,
CONCAT(dt.path,'/',d.dept_name)
FROM dept d
JOIN dept_tree dt
ON d.parent_id=dt.id
)
SELECT *
FROM dept_tree;
结果:
text
总公司
总公司/技术中心
总公司/技术中心/开发部
总公司/技术中心/开发部/Java组
十三、WITH RECURSIVE 能向上查吗?
答案:
完全可以。
很多人误以为只能向下查。
实际上:
递归方向由 JOIN 条件决定。
十四、向上递归查询祖先节点
例如:
查询 Java组(id=6) 的所有上级。
树:
text
总公司(1)
└── 技术中心(2)
└── 开发部(4)
└── Java组(6)
SQL:
sql
WITH RECURSIVE parent_tree AS (
SELECT
id,
dept_name,
parent_id
FROM dept
WHERE id = 6
UNION ALL
SELECT
d.id,
d.dept_name,
d.parent_id
FROM dept d
JOIN parent_tree pt
ON d.id = pt.parent_id
)
SELECT *
FROM parent_tree;
结果:
text
6 Java组
4 开发部
2 技术中心
1 总公司
十五、向上生成完整路径
sql
WITH RECURSIVE parent_tree AS (
SELECT
id,
dept_name,
parent_id,
dept_name AS path
FROM dept
WHERE id=6
UNION ALL
SELECT
d.id,
d.dept_name,
d.parent_id,
CONCAT(d.dept_name,'/',pt.path)
FROM dept d
JOIN parent_tree pt
ON d.id=pt.parent_id
)
SELECT *
FROM parent_tree
ORDER BY id;
最终得到:
text
总公司/技术中心/开发部/Java组
十六、避免死循环
假设数据错误:
text
1 -> 2
2 -> 3
3 -> 1
形成环:
text
1
↓
2
↓
3
↑
└───
递归将无限执行。
解决方法:
记录访问路径。
sql
WITH RECURSIVE dept_tree AS (
SELECT
id,
parent_id,
CAST(id AS CHAR(1000)) path
FROM dept
WHERE id=1
UNION ALL
SELECT
d.id,
d.parent_id,
CONCAT(dt.path,',',d.id)
FROM dept d
JOIN dept_tree dt
ON d.parent_id=dt.id
WHERE FIND_IN_SET(d.id,dt.path)=0
)
SELECT *
FROM dept_tree;
十七、递归层数限制
查看:
sql
SHOW VARIABLES LIKE '%recursion%';
通常:
text
cte_max_recursion_depth = 1000
表示最多递归1000层。
修改:
sql
SET SESSION cte_max_recursion_depth = 5000;
或者:
sql
SET GLOBAL cte_max_recursion_depth = 5000;
十八、WITH RECURSIVE 使用规则总结
必须:
sql
WITH RECURSIVE name AS(
anchor
UNION ALL
recursive
)
递归部分必须引用自己
sql
FROM name
必须有终止条件
sql
WHERE level < 100
否则死循环。
推荐使用:
sql
UNION ALL
而不是:
sql
UNION
因为:
sql
UNION
需要去重。
性能更差。
十九、企业开发中的典型应用
组织架构树
text
总公司
└── 分公司
└── 部门
查询所有下级。
RBAC权限菜单
text
系统管理
├── 用户管理
├── 角色管理
└── 权限管理
加载菜单树。
评论回复
text
评论
└── 回复
└── 回复
查询完整评论链。
行政区域
text
中国
└── 广东
└── 深圳
查询省市区。
商品分类
text
电子产品
└── 手机
└── 安卓手机
查询所有分类。
二十、面试高频问题
Q1:WITH RECURSIVE 从哪个版本开始支持?
MySQL 8.0。
Q2:WITH 和 WITH RECURSIVE 区别?
WITH
普通CTE,不递归。
sql
WITH t AS(
SELECT *
FROM user
)
SELECT * FROM t;
WITH RECURSIVE
支持递归调用自身。
Q3:能否查询父节点?
可以。
改变 JOIN 方向即可。
向下:
sql
d.parent_id = tree.id
向上:
sql
d.id = tree.parent_id
Q4:为什么推荐 WITH RECURSIVE?
相比循环查询:
- SQL更简洁
- 只访问一次数据库
- 性能更好
- 天然支持树形结构
总结
WITH RECURSIVE 是 MySQL 8.0 引入的递归查询能力,通过"锚点查询 + UNION ALL + 递归查询"的方式,可以优雅地处理组织架构、菜单树、评论树、行政区划、商品分类等层级数据,既能向下查询所有子孙节点,也能向上查询所有祖先节点,是现代 MySQL 树形数据查询的首选方案。
参考: