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


最新评论