首页/数据/SQL 速查/递归 CTE:树形结构展开

递归 CTE:树形结构展开

WITH RECURSIVE 以“初始行 + 递归项”逐步展开层级数据,典型场景是组织架构、分类树、物料清单。

SQL 速查数据更新于 2026-10-13

递归 CTE 是什么

WITH RECURSIVE 用“初始行 + 递归项”的方式把层级数据一层层展开:先取根节点,再用上一轮的结果连接下一层,直到没有新行为止。组织架构、分类树、物料清单、路径回溯都靠它。MySQL 8.0 与 PostgreSQL 都支持,关键词 RECURSIVE 必须写。

基本信息

写法WITH RECURSIVE 名字 AS (初始查询 UNION ALL 递归查询)
自引用递归项里 FROM 这个 CTE 名,代表上一轮新产出的行
UNION ALL不去重、性能好;换成 UNION 会去重,可防环但代价更高
收敛方式由连接条件自然收敛,或加深度上限显式刹车
深度限制MySQL 有 cte_max_recursion_depth,默认 1000,超出报错
列类型两部分对应列的类型要一致,否则推导结果可能不符预期

组织架构展开

组织架构展开
WITH RECURSIVE org AS (
    SELECT id, name, manager_id, 1 AS lvl     -- 初始行:根节点(没有上级)
    FROM employees
    WHERE manager_id IS NULL
    UNION ALL
    SELECT e.id, e.name, e.manager_id, o.lvl + 1   -- 递归项:引用上一轮结果
    FROM employees e
    JOIN org o ON e.manager_id = o.id              -- 连接键需要索引
)
SELECT lvl, id, name FROM org ORDER BY lvl, id;    -- 按层级输出

加深度上限防跑飞

加深度上限防跑飞
WITH RECURSIVE org AS (
    SELECT id, name, manager_id, 1 AS lvl
    FROM employees
    WHERE manager_id IS NULL
    UNION ALL
    SELECT e.id, e.name, e.manager_id, o.lvl + 1
    FROM employees e
    JOIN org o ON e.manager_id = o.id
    WHERE o.lvl < 10          -- 数据成环或关系写反时,这行能救回一条 SQL
)
SELECT lvl, id, name FROM org ORDER BY lvl;   -- 超过 10 层的数据不会出现在结果里

结构要点

组成部分说明注意事项
初始查询取根节点,如 manager_id IS NULL条件写错会直接返回空集
递归项连接 CTE 自身取下一层连接键没索引则每轮全表扫描
UNION ALL本轮行与历史行合并结果不去重,成环时会重复展开
深度列常见的 lvl 或 depth 计数列没有它就无法加上限
终止条件递归项返回空集即结束成环时永不返回空集,必须加上限

典型使用场景

  • 组织架构展开:统计各层级人数、列出某人全部下属
  • 分类树与行政区划树的层级遍历
  • 物料清单展开:从一个成品逐层算清所需零件
  • 路径回溯:从某节点向上拼出到根的完整路径字符串
  • 权限菜单树:按角色递归展开可见节点
  • 把扁平父子表转成层级结构,供前端树组件渲染
MySQL 8.0 与 PostgreSQL 都要求写 WITH RECURSIVE,漏掉 RECURSIVE 会直接语法报错;Oracle 与 SQL Server 的写法不同,跨库脚本需要按方言分别改写。
递归项不收敛时查询不会自己结束:数据成环、父子关系写反、两人互为上级都会让语句长时间运行。上线前务必带上 WHERE lvl < N 的深度上限,并确认连接键有索引。

易错点

  • 漏写 RECURSIVE 关键字:MySQL 直接报语法错误,写成 WITH 时必须带 RECURSIVE
  • 递归项连接条件不收敛:数据成环时语句一直跑,先加深度上限再执行
  • 递归连接键没有索引:每一层都要扫一遍全表,层级越深代价越明显
  • 忽视默认递归深度限制:MySQL 的 cte_max_recursion_depth 默认 1000,超限直接报错,需要按业务调整
  • 用 UNION 代替 UNION ALL 防环:付出全量去重的代价,正确做法是加深度上限

数据来源与参考

  • MySQL 8.0 Reference Manual · Recursive CTEs
  • PostgreSQL Documentation · Recursive Queries
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌