CTE+阶段式递归:用公共表表达式搞定复杂业务逻辑,告别SQL难题!

本文涉及的产品
RDS AI 助手,专业版
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
PolarDB Agent Express,2核4GB
简介: 数据库小学妹带你轻松掌握CTE(公共表表达式)与递归查询!从基础语法、多级嵌套到树形结构处理(如部门/商品分类),详解WITH、WITH RECURSIVE及阶段式递归实战。告别子查询嵌套地狱,提升可读性与性能,附避坑指南(索引、层级限制等)。MySQL 8.0+ 必学利器!

📌 今日关键词:CTE、公共表表达式、递归查询、阶段式递归、WITH、树形结构

大家好,我是 数据库小学妹 👋

前面我们学过子查询、窗口函数这些进阶技能。今天我要分享一个让我"相见恨晚"的功能 —— CTE(公共表表达式)+ 递归

为什么这么说?因为我一开始遇到树形结构数据(部门层级、商品分类、组织架构)的时候,子查询套子查询,写到最后自己都绕晕了,性能还差。后来发现了CTE+递归这个组合,SQL写得清爽多了!

今天小学妹就带你从CTE基础到递归实战,一步步把这个技能掌握。


一、CTE 是什么?告别嵌套地狱

啥是CTE?你就理解成给一段查询结果起个名字,后面想用直接写名字就行。

就像 Excel 里给某个区域起名,后面公式里直接用那个名,不用每次都重写那片区域。

基础语法

WITH employee_cte AS (
    SELECT 
        id, 
        name, 
        manager_id, 
        salary
    FROM employees
    WHERE manager_id IS NULL
)
SELECT * FROM employee_cte;

WITH 后面就是 CTE 的名字,AS 括号里是查询内容。最后用这个临时名字来查。

💡CTE 只在这次查询里有效,查完就没了,不会污染数据库。

CTE vs 子查询:有啥区别?

场景 CTE 子查询
代码可读性 清爽,一层一层 嵌套多了看瞎眼
复用性 一个 CTE 多地方引用 每次都要重写
调试 方便,单独查 CTE 麻烦,拆开要重寫
性能 差不多 差不多

用子查询套多了自己都看不下去,CTE 就是来解决这个问题的。


二、CTE 嵌套着用:复杂查询变简单

CTE 最实用的地方是可以一个接一个写,像搭积木一样。

💻 实战:部门薪资统计 + 排名

要把部门总薪资、平均薪资、排名全算出来,拆成三级 CTE:

WITH department_salary AS (
    SELECT 
        department_id,
        SUM(salary) as total_salary
    FROM employees
    GROUP BY department_id
),
average_salary AS (
    SELECT 
        department_id,
        total_salary,
        total_salary / COUNT(*) as avg_salary
    FROM department_salary
),
department_rank AS (
    SELECT 
        department_id,
        avg_salary,
        RANK() OVER (ORDER BY avg_salary DESC) as rank
    FROM average_salary
)
SELECT * FROM department_rank;

第一层算总薪资,第二层算平均,第三层加排名。一层一层往下走,每层干一件事,逻辑清清楚楚。

💡CTE 之间可以互相引用。后面的 CTE 可以直接用前面 CTE 的名字,就像引用表一样。

配合 CASE WHEN 做数据分类

WITH employee_data AS (
    SELECT 
        id,
        name,
        salary,
        CASE 
            WHEN salary > 10000 THEN '高薪'
            WHEN salary > 5000 THEN '中薪'
            ELSE '低薪'
        END as salary_level
    FROM employees
),
high_salary_employees AS (
    SELECT *
    FROM employee_data
    WHERE salary_level = '高薪'
)
SELECT * FROM high_salary_employees;

第一层先分类,第二层再筛选。写起来比嵌套子查询顺多了。


三、递归 CTE:处理树形结构的神器

递归 CTE 是 CTE 的进阶用法,专门用来查层级数据 —— 组织架构、商品分类、审批流程这些场景太常用了!

语法结构

WITH RECURSIVE recursive_cte AS (
    -- 基础查询(起点)
    SELECT 
        id,
        name,
        manager_id,
        1 as level
    FROM employees
    WHERE manager_id IS NULL

    UNION ALL

    -- 递归部分(自己调用自己)
    SELECT 
        e.id,
        e.name,
        e.manager_id,
        r.level + 1
    FROM employees e
    INNER JOIN recursive_cte r ON e.manager_id = r.id
)
SELECT * FROM recursive_cte;

分两部分:

  1. 基础查询:先找到"起点"(没有上级的节点)
  2. 递归部分:用起点往下找,一层一层查,找不到新数据就停

💡 递归的逻辑就像你查家谱:先找到太爷爷(起点),然后一层层往下找子子孙孙。

💻 实战:部门层级查询

WITH RECURSIVE department_tree AS (
    SELECT 
        id,
        name,
        parent_id,
        1 as level
    FROM departments
    WHERE parent_id IS NULL

    UNION ALL

    SELECT 
        d.id,
        d.name,
        d.parent_id,
        dt.level + 1
    FROM departments d
    INNER JOIN department_tree dt ON d.parent_id = dt.id
)
SELECT * FROM department_tree;

跑出来的结果:

id name parent_id level
1 总部 NULL 1
2 销售部 1 2
3 技术部 1 2
4 UI 组 2 3
5 前端组 2 3
6 后端组 3 3

以前实现这个要写存储过程或者复杂的自连接,现在一行 WITH RECURSIVE 搞定。做权限树、商品分类的同学,这个技能必须有!


四、阶段式递归的实战场景

📚 场景一:商品分类树

和部门层级类似,就是把部门换成商品:

WITH RECURSIVE product_tree AS (
    SELECT 
        id,
        name,
        parent_id,
        1 as level
    FROM products
    WHERE parent_id IS NULL

    UNION ALL

    SELECT 
        p.id,
        p.name,
        p.parent_id,
        pt.level + 1
    FROM products p
    INNER JOIN product_tree pt ON p.parent_id = pt.id
)
SELECT * FROM product_tree;

📚 场景二:数据溯源

排查数据问题时经常要用 —— 找到某个记录的来源,一层一层往上找:

WITH RECURSIVE data_trace AS (
    SELECT 
        id,
        data,
        parent_id,
        1 as trace_level
    FROM audit_log
    WHERE id = 12345

    UNION ALL

    SELECT 
        a.id,
        a.data,
        a.parent_id,
        dt.trace_level + 1
    FROM audit_log a
    INNER JOIN data_trace dt ON a.id = dt.parent_id
)
SELECT * FROM data_trace;

💡 这个是"向上追溯",和前面的"向下展开"方向相反。核心区别在 JOIN 条件上:向下查是 子.parent_id = 父.id,向上查是 父.id = 子.parent_id

📚 场景三:多阶段业务逻辑拆解

客户分层这种需求,拆成几步更清楚:

WITH stage1 AS (
    SELECT id, name, email, created_at
    FROM customers
    WHERE status = 'active'
),
stage2 AS (
    SELECT 
        c.id,
        c.name,
        SUM(o.amount) as total_spent
    FROM stage1 c
    LEFT JOIN orders o ON c.id = o.customer_id
    GROUP BY c.id, c.name
),
stage3 AS (
    SELECT 
        id,
        name,
        total_spent,
        CASE 
            WHEN total_spent > 10000 THEN 'VIP'
            WHEN total_spent > 5000 THEN '普通 VIP'
            WHEN total_spent > 1000 THEN '新客户'
            ELSE '潜在客户'
        END as customer_level
    FROM stage2
)
SELECT * FROM stage3;

第一层筛活跃客户,第二层算消费总额,第三层打标签。每一步干干净净,改逻辑也方便。

📚 场景四:审批流程追踪

WITH RECURSIVE approval_trace AS (
    SELECT 
        id,
        process_id,
        user_id,
        status,
        1 as stage
    FROM approvals
    WHERE process_id = 'P12345'
      AND status = 'pending'

    UNION ALL

    SELECT 
        a.id,
        a.process_id,
        a.user_id,
        a.status,
        at.stage + 1
    FROM approvals a
    INNER JOIN approval_trace at ON 
        a.process_id = at.process_id 
        AND a.id = at.next_approval_id
)
SELECT * FROM approval_trace;

这个在公司内部系统里很常用,查一条审批流到了哪一步、还有谁需要审批。


五、CTE + 窗口函数:强强联合

CTE 和窗口函数不冲突,经常混着用。CTE 负责拆分逻辑,窗口函数负责排名聚合。

比如同时做部门统计和员工排名:

WITH employee_cte AS (
    SELECT 
        id,
        name,
        department_id,
        salary,
        COUNT(*) OVER (PARTITION BY department_id) as dept_count,
        SUM(salary) OVER (PARTITION BY department_id) as dept_total
    FROM employees
),
ranked_employees AS (
    SELECT 
        id,
        name,
        department_id,
        salary,
        ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) as rank
    FROM employee_cte
)
SELECT * FROM ranked_employees;

六、新手避坑指南

❌ 坑一:忘记加递归限制

不加限制的话,万一数据有环,查起来就停不了了:

-- 错误示例:无限递归
WITH RECURSIVE infinite_loop AS (
    SELECT id, name FROM departments
    UNION ALL
    SELECT id, name FROM infinite_loop
)
SELECT * FROM infinite_loop;
-- ✅ 正确写法:加递归限制
WITH RECURSIVE safe_loop AS (
    SELECT 
        id, name, 1 as level
    FROM departments
    UNION ALL
    SELECT 
        id, name, sl.level + 1
    FROM departments d
    INNER JOIN safe_loop sl ON d.parent_id = sl.id
    WHERE sl.level < 10
)
SELECT * FROM safe_loop;

❌ 坑二:用了 UNION 而不是 UNION ALL

UNION 要去重,多一层开销。递归 CTE 里基本都用 UNION ALL。

❌ 坑三:递归字段没建索引

递归字段(比如 parent_id、manager_id)一定要建索引,不然递归查询会慢到怀疑人生。

CREATE INDEX idx_parent_id ON departments(parent_id);
CREATE INDEX idx_manager_id ON employees(manager_id);

❌ 坑四:MySQL 版本不支持

CTE 是 MySQL 8.0 才有的功能!如果你还在用 5.7,升级或者用其他方式替代。


七、今日学习心得

  1. CTE 让复杂查询变清爽,一层一层写,比嵌套子查询好维护多了
  2. 递归 CTE 是树形数据的好工具,组织架构、商品分类、审批流程都能用
  3. 阶段式拆解是写 SQL 的好习惯,复杂业务拆成几步,每步干净利落
  4. 注意加递归限制和建索引,这两个坑我踩过,别让大家再踩了
  5. CTE + 窗口函数 组合起来,能处理更多场景

👋 我是 数据库小学妹,一个用设计师思维学数据库的转行人。我们一起,把复杂的技术变得简单有趣!💕


本文为个人学习总结,所有示例基于 MySQL 8.0+。如果你的版本低于 8.0,CTE 功能不可用,建议升级或使用其他方式替代。

相关文章
|
9天前
|
人工智能 开发工具 iOS开发
Claude Code 新手完全上手指南:安装、国产模型配置与常用命令全解
Claude Code 是一款运行在终端环境中的 AI 编程助手,能够直接在命令行中完成代码生成、项目分析、文件修改、命令执行、Git 管理等开发全流程工作。它最大的特点是**任务驱动、终端原生、轻量高效、多模型兼容**,无需图形界面、不依赖 IDE 插件,能够深度融入开发者日常工作流。
3136 8
|
12天前
|
Shell API 开发工具
Claude Code 快速上手指南(新手友好版)
AI编程工具卷疯啦!Claude Code凭借任务驱动+终端原生的特性,成了开发者的效率搭子。本文从安装、登录、切换国产模型到常用命令,手把手带新手快速上手,全程避坑,30分钟独立用起来。
3198 20
|
5天前
|
人工智能 Linux BI
国内用 Claude Code 终于不用翻墙了:一行命令搞定,自动接 DeepSeek
JeecgBoot AI专题研究 一键脚本:Claude Code + JeecgBoot Skills + DeepSeek 全平台接入 一行命令装好 Claude Code + JeecgBoot Skills + DeepSeek 接入,无需翻墙使用 Claude Code,支持 Wind
2130 3
国内用 Claude Code 终于不用翻墙了:一行命令搞定,自动接 DeepSeek
|
24天前
|
人工智能 JSON 供应链
畅用7个月无影 JVS Claw |手把手教你把JVS改造成「科研与产业地理情报可视化大师」
LucianaiB分享零成本畅用JVS Claw教程(学生认证享7个月使用权),并开源GeoMind项目——将JVS改造为科研与产业地理情报可视化AI助手,支持飞书文档解析、地理编码与腾讯地图可视化,助力产业关系图谱构建。
23591 15
畅用7个月无影 JVS Claw |手把手教你把JVS改造成「科研与产业地理情报可视化大师」
|
1天前
|
人工智能 自然语言处理 文字识别
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
Qwen3.7-Max是阿里云百炼面向智能体时代推出的新一代旗舰模型,对标GPT-5.5、Claude Opus 4.7等闭源旗舰。该模型支持百万级token上下文窗口,具备顶级推理能力、多模态搜索与视觉理解增强、流式输出低延迟响应等核心优势,覆盖编程、办公、长周期自主执行等复杂场景。同时支持OpenAI接口兼容,便于系统快速迁移。用户可通过Token Plan团队版、Coding Plan或节省计划等订阅方式灵活调用,适合企业级高要求场景使用。
|
11天前
|
人工智能 JSON BI
DeepSeek V4-Pro 接入 Claude Code 完全实战:体验、测试与关键避坑指南
Claude Code 作为当前主流的 AI 编程辅助工具,凭借强大的代码理解、工程执行与自动化能力深受开发者喜爱,但原生模型的使用成本相对较高。为了在保持能力的同时进一步降低开销,不少开发者开始寻找兼容度高、价格更友好的替代模型。DeepSeek V4 系列的发布带来了新的选择,该系列包含 V4-Pro 与 V4-Flash 两款模型,并提供了与 Anthropic 完全兼容的 API 接口,理论上只需简单修改配置,即可让 Claude Code 无缝切换为 DeepSeek 引擎。
2645 3
|
3天前
|
人工智能 自然语言处理 安全
Claude Code 全攻略:命令大全+三种模式+记忆体系+实战工作流完整手册
Claude Code 是当前最流行的终端级 AI 编程助手,能够直接在命令行中完成代码生成、项目理解、文件修改、命令执行、错误修复等全流程开发工作。它不依赖图形界面、不占用额外资源,却能深度理解项目结构,自动生成规范代码,大幅提升研发效率。
772 2
|
10天前
|
人工智能 安全 开发工具
Claude Code 官方工作原理与使用指南
Claude Code 不是传统代码补全工具,而是 Anthropic 推出的终端 AI 代理,具备代理循环、双驱动架构(模型+工具)、全局项目感知、6 种权限模式等核心能力,本文基于官方文档系统解析其工作原理与高效使用技巧。
1443 0