在关系型数据库中,层级数据如组织结构、商品分类常通过自关联表存储。使用WITH RECURSIVE可以写出递归子查询,从一个锚点出发不断联结自身,直到满足终止条件,从而一次性取出整棵子树。

WITH RECURSIVE基本语法
递归公共表表达式由两部分组成:锚点成员和递归成员,用UNION ALL连接。锚点先查出初始行,递归部分引用CTE自身继续扩展。
WITH RECURSIVE cte_name AS ( -- 锚点查询 SELECT id, parent_id, name FROM categories WHERE parent_id IS NULL UNION ALL -- 递归查询 SELECT c.id, c.parent_id, c.name FROM categories c INNER JOIN cte_name ON c.parent_id = cte_name.id ) SELECT * FROM cte_name;
员工层级示例
假设有员工表emp(id, manager_id, name),要查出某领导及其所有下属,可这样写:
WITH RECURSIVE sub_tree AS ( SELECT id, manager_id, name FROM emp WHERE id = 1 UNION ALL SELECT e.id, e.manager_id, e.name FROM emp e JOIN sub_tree s ON e.manager_id = s.id ) SELECT * FROM sub_tree;
避免死循环
若数据存在环,递归不会停止。可在SELECT中记录访问路径,遇到重复id即截断。
WITH RECURSIVE sub_tree AS ( SELECT id, manager_id, name, ARRAY[id] AS path FROM emp WHERE id = 1 UNION ALL SELECT e.id, e.manager_id, e.name, s.path || e.id FROM emp e JOIN sub_tree s ON e.manager_id = s.id WHERE e.id <> ALL(s.path) ) SELECT * FROM sub_tree;
执行原理简述
数据库先执行锚点,把结果放入工作表;再用工作表驱动递归成员,新结果回到工作表,重复直至为空。理解这点有助于控制深度与性能。
| 阶段 | 动作 |
|---|---|
| 初始化 | 运行锚点,填充基础行 |
| 迭代 | 用上轮结果联结,产生新行 |
| 终止 | 无新行时结束,返回CTE全部内容 |
使用注意
- 必须用UNION ALL,UNION会去重但拖慢递归。
- 递归成员中不要对CTE做聚合或排序,多数引擎不支持。
- 数据量大时建议加深度字段限制层数。
WITH RECURSIVE不是某个数据库私有特性,PostgreSQL、MySQL 8.0、SQL Server均支持类似写法,只是细节略有差异。
掌握上述方法后,便能用标准SQL优雅解决绝大多数层级展开问题,而不必在应用代码里做多次查询。
SQLWITH_RECURSIVE递归查询修改时间:2026-07-28 01:54:19