SQL语句中实现递归查询操作方法
作者:rchmin
这篇文章给大家介绍SQL语句中实现递归查询操作方法,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
在SQL中实现递归查询操作,通常有两种主要的方法:使用递归公用表表达式(Recursive Common Table Expressions,CTEs)和递归查询函数(例如,在PostgreSQL中使用WITH RECURSIVE,在SQL Server中使用WITH RECURSIVE或CTE,而在Oracle中使用CONNECT BY)。
数据表定义:
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(50),
manager_id INT -- 指向上级,CEO的manager_id为NULL(或0,通常用NULL表示无上级)
);初始化数据:
INSERT INTO `employees` (`id`, `name`, `manager_id`) VALUES (101, '马总', NULL); INSERT INTO `employees` (`id`, `name`, `manager_id`) VALUES (201, '张工', 101); INSERT INTO `employees` (`id`, `name`, `manager_id`) VALUES (202, '王工', 101); INSERT INTO `employees` (`id`, `name`, `manager_id`) VALUES (301, '李工', 201); INSERT INTO `employees` (`id`, `name`, `manager_id`) VALUES (401, '赵工', 301); INSERT INTO `employees` (`id`, `name`, `manager_id`) VALUES (402, '刘工', 301);
查询需求:
请查询出id=101的人员及其所有的下级信息(包括间接下级),同时标记出下属层级,自身层级设为0;
查询语句(MySQL 8.0+):
要查询 id = 101 的所有下级(包括直接和间接下级),并标记出每个下属所处的层级(1 表示直接下级,2 表示下级的下级,依此类推),可以使用递归公用表表达式(Recursive CTE)。以下是符合要求的 SQL 语句:
WITH RECURSIVE subordinates AS (
-- 初始查询:找到直接下级(manager_id = 101)
SELECT
id,
name,
manager_id,
0 AS level
FROM employees
WHERE id = 101
UNION ALL
-- 递归查询:找到下一级下属
SELECT
e.id,
e.name,
e.manager_id,
s.level + 1
FROM employees e
INNER JOIN subordinates s ON e.manager_id = s.id
)
SELECT *
FROM subordinates
ORDER BY level, id;执行结果:

语法解释:
WITH RECURSIVE subordinates AS (...) 是 SQL 中用于定义递归公用表表达式(Recursive Common Table Expression)的语法。下面逐部分解释:
1.WITH关键字
- 表示定义一个公用表表达式(CTE,Common Table Expression),类似于一个临时的命名结果集,可以在后续的查询中引用。
- 通常用于简化复杂查询,提高可读性。
2.RECURSIVE关键字
- 指明该 CTE 是递归的,即它可以在自己的定义中引用自身,从而实现迭代或层次遍历。
- 大部分主流数据库(如 PostgreSQL、MySQL 8.0+、SQL Server、Oracle)都支持该语法,但 MySQL 中必须明确写出
RECURSIVE,而 SQL Server 中可省略(但需要其他方式表示递归)。
3.subordinates– CTE 名称(自定义)
- 给这个临时结果集取名为
subordinates,后续的SELECT语句就可以像使用普通表一样使用它。
4. 括号内的递归定义
递归 CTE 通常由两部分组成,用 UNION ALL 连接:
(1)锚点成员(非递归部分)
SELECT id, name, manager_id, 1 AS level FROM employees WHERE id = 101
- 这是递归的起点,首先查询出
id = 101的员工(即 101 自身)。 - 这里手动标记
level = 0,表示第 0 层级。 - 锚点成员只执行一次,其结果集作为递归的初始数据。
(2)递归成员(递归部分)
SELECT e.id, e.name, e.manager_id, s.level + 1 FROM employees e INNER JOIN subordinates s ON e.manager_id = s.id
- 递归成员会反复执行,直到不再返回新行。
- 每次迭代中,它只将上一轮
subordinates结果集中的每一行(作为上级s)与employees表连接,找出这些上级的下级员工(e.manager_id = s.id)。 - 同时,层级别为
s.level + 1,表示比上一级深一层。 - 新找到的行被加入
subordinates结果集,并用于下一次迭代。
(3) 终止条件
- 当某次迭代没有产生任何新行时,递归自动停止。
- 要求数据中没有循环引用(例如 A 的上级是 B,B 的上级又是 A),否则可能导致无限递归。大多数数据库默认设置了递归深度限制(如 MySQL 的
cte_max_recursion_depth)。
5. 最终引用
定义完 subordinates 后,外层的 SELECT * FROM subordinates 将返回所有递归收集到的行。
示例执行流程
假设 employees 表有数据:
- id=101 管理 201,202
- 201 管理 301
- 301 管理 401,402
执行过程:
- 锚点:找到 201、202,level=1。
subordinates当前有 (201, ..., 1), (202, ..., 1)。- 第一次递归:以 201、202 为上级找下级 → 找到 301(上级 201),level=2。
subordinates新增 (301, ..., 2)。- 第二次递归:以 301 为上级找下级 → 找到 401,402,level=3。
subordinates新增 (401, ..., 3),(402, ..., 3)。- 第三次递归:以 401、402 为上级找下级 → 无结果,终止。
- 最终输出所有行。
适用场景
- 组织架构(上级-下级)
- 物料清单(BOM)
- 树形结构遍历(评论回复、分类层级)
- 图或路径查询
WITH RECURSIVE 是处理此类分层或递归查询的标准 SQL 方法,比使用游标或多次自连接更加简洁高效。
到此这篇关于SQL语句中如何实现递归查询操作的文章就介绍到这了,更多相关SQL递归查询内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
