如何利用MySQL 8.0公用表表达式_通过WITH语句提升SQL代码可读性

夏涛同学_8556

夏涛同学_8556

2026-05-31

740人浏览

原创

with能显著提升sql可读性与维护性,但仅适用于重复子查询、多层嵌套及逻辑分段场景;滥用(如单次引用、相关子查询)反而降低性能与可调试性。

如何利用mysql 8.0公用表表达式_通过with语句提升sql代码可读性

直接说结论:用 WITH 把重复子查询、多层嵌套和逻辑分段抽出来,SQL 就会立刻变清晰——但前提是别把 CTE 当成万能胶水,乱套反而更难 debug。

为什么嵌套子查询让人头疼?

比如统计每个客户的订单数 + 最高单笔金额 + 首次下单时间,不用 WITH 时容易写成三层嵌套:

SELECT 
  c.name,
  (SELECT COUNT(*) FROM orders o WHERE o.customer_id = c.id) AS order_count,
  (SELECT MAX(amount) FROM orders o WHERE o.customer_id = c.id) AS max_amount,
  (SELECT MIN(created_at) FROM orders o WHERE o.customer_id = c.id) AS first_order
FROM customers c;

问题不只在写法丑:每次子查询都全表扫描 orders,性能差;字段名分散在三处,改一个得找三遍;无法复用中间结果(比如想加个“是否 VIP”判断,还得再写一遍 COUNT)。

用 WITH 拆开后,逻辑就平铺直叙:

  • 先定义 customer_stats 算出每个客户的基础聚合
  • 主查询只管关联和展示,不掺杂计算逻辑
  • 后续加新字段(如 is_vip)直接在 customer_stats 里加一列就行

多个 CTE 怎么组织才不混乱?

当要拼接客户信息、订单汇总、地区销量排名三块数据时,别堆在一个 CTE 里硬塞。正确做法是按职责拆:

WITH 
  customer_orders AS (
    SELECT customer_id, COUNT(*) AS cnt, SUM(amount) AS total
    FROM orders GROUP BY customer_id
  ),
  region_sales AS (
    SELECT r.name AS region, SUM(o.amount) AS sales
    FROM orders o JOIN stores s ON o.store_id = s.id
    JOIN regions r ON s.region_id = r.id
    GROUP BY r.name
  ),
  top_customers AS (
    SELECT customer_id, total
    FROM customer_orders
    ORDER BY total DESC LIMIT 10
  )
SELECT c.name, co.cnt, co.total, rs.region
FROM customers c
JOIN customer_orders co ON c.id = co.customer_id
LEFT JOIN top_customers tc ON c.id = tc.customer_id
LEFT JOIN stores s ON c.preferred_store = s.id
LEFT JOIN regions rs ON s.region_id = rs.id;

关键点:

  • 每个 CTE 名称必须见名知意(customer_orders 而不是 tmp1)
  • CTE 之间可以互相引用(top_customers 基于 customer_orders),但不能循环依赖
  • 如果某个 CTE 只被用一次且逻辑极简(比如只 SELECT 1 AS flag),不如直接写进主查询——CTE 不是越多越好

递归 CTE 容易卡死,怎么防?

WITH RECURSIVE 看似强大,但生产环境最常踩的坑是无限递归。比如查部门树时,若数据里存在 parent_id = id 的脏数据,查询不会报错,只会跑到 cte_max_recursion_depth 限制才停,期间 CPU 暴涨。

MySQL
MySQL

编写正确的MySQL查询,避免字符集、索引和锁方面的常见陷阱。

下载

安全写法必须带两层兜底:

  • 显式限制递归深度:WHERE level (比依赖全局变量更可控)
  • 用 NOT EXISTS 或 LEFT JOIN ... IS NULL 检查自引用,提前过滤掉异常节点
  • 始终在递归分支的 SELECT 中包含 level 或 path 字段,方便排查哪一层崩了

例如组织树查询中,加一行 AND o.id != o.parent_id 就能避开最常见自环:

WITH RECURSIVE org_tree AS (
  SELECT id, name, parent_id, 1 AS level, CAST(id AS CHAR(200)) AS path
  FROM org WHERE parent_id IS NULL
  UNION ALL
  SELECT o.id, o.name, o.parent_id, ot.level + 1, CONCAT(ot.path, '-', o.id)
  FROM org o
  INNER JOIN org_tree ot ON o.parent_id = ot.id
  WHERE o.id != o.parent_id AND ot.level <h3>CTE 和窗口函数混用时的陷阱</h3><p>很多人想在 CTE 里直接用 <code>ROW_NUMBER() OVER(...)</code> 再在外面过滤,结果发现 <code>WHERE row_num = 1</code> 报错——因为窗口函数执行顺序晚于 <code>WHERE</code>,CTE 里的别名在外部不可直接用于过滤。</p><p>正确姿势只有两种:</p>
  • 把窗口函数放在 CTE 里,外部用 HAVING 或子查询包装(推荐):
WITH ranked_orders AS (
  SELECT 
    customer_id,
    amount,
    ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY created_at DESC) AS rn
  FROM orders
)
SELECT customer_id, amount 
FROM ranked_orders 
WHERE rn = 1;
  • 或者干脆不用 CTE,把 ROW_NUMBER() 放在主查询的 SELECT 里,再套一层子查询

注意:ORDER BY 在 CTE 内部无效(MySQL 8.0 不允许 CTE 子句含 ORDER BY),排序必须放在最终 SELECT 或窗口函数的 OVER 中。

真正难处理的从来不是语法,而是当你把五六个 CTE 串在一起、又嵌了窗口函数、还加了递归时,没人能一眼看懂数据从哪来、到哪去、在哪断的。这时候宁可多拆一个 CTE,也不要在一行里塞三个 COALESCE 套嵌套。

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

4063

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

851

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

1049

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

5921

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

2843

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

5900

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

7881

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1070

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

932

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL vs MySQL
PostgreSQL vs MySQL

共1课时 | 180人学习

使用phpenv集成环境安装极致CMS
使用phpenv集成环境安装极致CMS

共2课时 | 287人学习