必须用with recursive仅当需遍历未知深度的树形结构,如动态层级的员工下级、不定长邀请链、服务端生成序列;固定层级或分步聚合场景则嵌套查询或普通cte更优。

递归CTE不是“比嵌套查询更高效”的替代品,而是解决完全不同的问题——你要查的是固定层级的静态关系,还是动态深度的树形结构?选错就等于用锤子拧螺丝。
什么时候必须用 WITH RECURSIVE?
只有当你需要遍历未知深度的层级时,WITH RECURSIVE 才是唯一合理选择。所谓“嵌套查询”根本无法表达这种逻辑。
- 查某员工的所有下级(可能 1 层,也可能 7 层),且层级数不提前知道 → 必须用
WITH RECURSIVE - 展开用户邀请链路:
A→B→C→D→E,链长不定 →WITH RECURSIVE天然支持 - 生成日期序列或数字序列(如本月每天、1 到 1000)→
WITH RECURSIVE在服务端内存完成,无 I/O - 用多层
JOIN或子查询硬写 5 层关联 → 不是“嵌套查询”,是反模式:代码不可维护、无法适配新层级、性能随深度指数恶化
什么时候嵌套查询反而更合适?
如果你的问题本质是“分步聚合 + 条件筛选”,没有层级依赖关系,嵌套查询或普通 CTE 更直接、更可控。
- 先算部门平均薪资,再筛出高于公司均值的部门 → 用两个普通 CTE(
dept_avg和company_avg)就够了 - 从订单中提取首单时间,再统计首月消费总额 → 多层子查询或非递归 CTE 更清晰,无需递归开销
- 数据库版本低于 MySQL 8.0 或 PostgreSQL 8.4 →
WITH RECURSIVE根本不可用,嵌套子查询是唯一选项 - 锚成员或递归条件无法走索引 → 强行上
WITH RECURSIVE可能比三层子查询还慢,因为每轮都在膨胀结果集上全表匹配
WITH RECURSIVE 容易被忽略的硬性前提
它不是开了就能快,性能优势全系于两处是否可控:
-
anchor member(初始查询)必须能命中索引,否则第一层就扫全表,后续所有递归都白搭 - 递归部分的
JOIN条件(如ON d.parent_id = h.id)必须有对应索引,否则每轮都做嵌套循环匹配 - MySQL 默认禁用递归:必须显式执行
SET SESSION cte_max_recursion_depth = 100,否则直接报错 - PostgreSQL 需加
SEARCH DEPTH FIRST BY id SET seq才启用路径优化,否则默认无序展开,可能重复计算
真正难的不是写对语法,而是判断“这到底是不是树”。很多所谓“层级”其实是扁平关联(比如订单+商品+类目三级关联),强行递归反而绕远路。先画出数据关系图,再决定用哪条路。










