首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >一条SQL背后的秘密:MySQL执行顺序解析

一条SQL背后的秘密:MySQL执行顺序解析

作者头像
俊才
发布2026-01-27 15:01:30
发布2026-01-27 15:01:30
1890
举报
文章被收录于专栏:数据库干货铺数据库干货铺

本文首发于「数据库干货铺」公众号,转载请联系授权

你是否曾写过复杂的SQL,却对结果感到困惑?比如明明加了 WHERE 条件,为什么 HAVING 还能“看到”聚合后的数据?又或者,为什么 SELECT 里定义的别名可以在 HAVING 中使用,却不能在 WHERE 中引用?

今天,我们就以一条真实业务场景中的SQL为例,彻底搞懂MySQL中SQL语句的逻辑执行顺序。这不是简单的语法罗列,而是带你穿透表象,理解数据库引擎如何一步步处理你的查询。

一、 从示例SQL谈起

先看一下以下这个SQL

代码语言:javascript
复制
SELECT DISTINCT
    s.shop_name,
    c.region,
    SUM(o.amount) AS total_amount,
    RANK() OVER (PARTITION BY s.shop_name ORDER BY SUM(o.amount) DESC) AS region_rank
FROM
    shops s
JOIN
    orders o ON s.shop_id = o.shop_id
JOIN
    customers c ON o.customer_id = c.customer_id
WHERE
    o.order_date >= '2026-01-01'
GROUP BY
    s.shop_name, c.region
WITH ROLLUP
HAVING
    total_amount > 1000
ORDER BY
    s.shop_name IS NULL,
    s.shop_name,
    c.region IS NULL,
    c.region
LIMIT 20 OFFSET 0;

这条SQL包含了多个表连接、条件过滤、分组统计、窗口函数和排序分页。如果我们不了解MySQL的执行顺序,就很难理解为什么WHERE后面不能使用SELECT中定义的别名,而HAVING却可以。

1. SQL书写顺序 vs 执行顺序:天壤之别

大多数SQL编写者都遵循一种常见的书写顺序:SELECT -> FROM -> WHERE -> GROUP BY -> HAVING -> ORDER BY -> LIMIT。但这仅仅是书写习惯,并非MySQL的实际执行顺序!

MySQL的实际执行顺序是这样的:

代码语言:javascript
复制
FROM -> ON -> JOIN 
-> WHERE -> GROUP BY
 -> WITH ROLLUP -> HAVING 
 -> SELECT -> DISTINCT 
 -> ORDER BY -> LIMIT

可以看到,MySQL的执行顺序与我们的书写顺序有显著差异。让我们通过一个直观的流程图来理解这一过程:

2. 深入解析MySQL执行流程

2.1 第一步:数据源的连接与过滤(FROM, ON, JOIN)

MySQL的执行过程从FROM子句开始。它首先对FROM子句中的表进行笛卡尔积操作,然后应用ON条件进行过滤。以我们的示例SQL为例:

代码语言:javascript
复制
FROM
    shops s
JOIN
    orders o ON s.shop_id = o.shop_id
JOIN
    customers c ON o.customer_id = c.customer_id

MySQL首先会加载shops、orders和customers三张表,生成一个包含所有可能组合的临时表(VT1)。然后应用ON条件过滤掉不匹配的记录,生成VT2。

实用技巧:当FROM子句包含多个表时,MySQL的执行顺序是从后往前、从右到左。因此,应该将数据量最小的表放在最后面,作为驱动表。

2.2 第二步:数据行过滤(WHERE)

WHERE子句在JOIN操作后执行,用于过滤掉不符合条件的行。在我们的例子中:

代码语言:javascript
复制
WHERE
    o.order_date >= '2026-01-01'

这一步会从中间表中筛选出2026年1月1日以后的订单记录。

注意: 由于WHERE子句在SELECT之前执行,因此不能在WHERE中使用SELECT中定义的列别名。这也是许多初学者容易犯错的地方。

2.3 第三步:数据分组(GROUP BY与WITH ROLLUP)

GROUP BY将数据按照指定的列进行分组,生成一个新的临时表。每组在结果集中只包含一行。在我们的例子中:

代码语言:javascript
复制
GROUP BY
    s.shop_name, c.region
WITH ROLLUP

这会先按店铺名称分组,再按区域分组。WITH ROLLUP选项会生成小计行(region为NULL)和总计行(shop_name和region都为NULL)。

关键点:从这一步开始,后续操作只能使用GROUP BY中的列或聚合函数。

2.4 第四步:分组结果过滤(HAVING)

HAVING用于对分组后的结果进行过滤,与WHERE类似,但它是在分组后执行,可以使用聚合函数。在我们的例子中:

代码语言:javascript
复制
HAVING
    total_amount > 1000

MySQL允许在HAVING中使用 SELECT列别名;这里使用了SELECT中定义的别名total_amount,所以可以使用别名。

2.5 第五步:选择与去重(SELECT与DISTINCT)

这是MySQL第一次处理SELECT子句,包括计算表达式、调用函数和生成列别名。随后,DISTINCT会去除重复行。需要注意的是,如果已经使用了GROUP BY,通常不需要再使用DISTINCT,因为分组后的每组数据已经是唯一的。

2.6 第六步:结果排序与分页(ORDER BY与LIMIT)

最后,ORDER BY对结果进行排序。由于它在SELECT之后执行,所以可以使用SELECT中定义的别名。在我们的例子中:

代码语言:javascript
复制
ORDER BY
    s.shop_name IS NULL,  -- 总计行排最后
    s.shop_name,
    c.region IS NULL,     -- 小计行排在各商店末尾
    c.region

这种排序方式确保了小计和总计行出现在合适的位置。

最后,LIMIT子句用于限制返回的行数,完成整个查询过程

二、 MySQL的完整执行架构

除了上述SQL逻辑执行顺序外,了解MySQL的整体执行架构也很重要。总体的执行架构图如下(借用其他作者的图):

MySQL执行架构总体分为三层核心:连接层(接入)→ 服务层(处理逻辑)→ 存储引擎层(数据读写),最终与磁盘交互完成数据持久化。简化流程图如下:

各阶段核心作用如下:

  • 连接层(Connectors)

MySQL与客户端的交互入口,支持多种协议(如 TCP/IP、Unix Socket、JDBC/ODBC 等),负责接收客户端的 SQL 请求并转发给连接器

  • 连接器(Connection Manager)

验证客户端的用户名 / 密码、校验连接权限(如是否有权限连接该数据库)、维护连接(区分长连接 / 短连接,管理连接池)。如果是长连接,连接器会复用连接以减少认证开销,但需注意内存泄漏问题(可通过wait_timeout控制闲置连接超时)

  • 查询缓存(Query Cache)

缓存SELECT语句的结果(以SQL语句为Key,结果为Value),命中则直接返回,无需后续流程。MySQL8.0已彻底移除(因缓存失效频繁、维护成本高,实际命中率极低),5.x 版本也建议关闭(query_cache_type=0)

  • 分析器 / 解析器(Parser)

词法分析:把 SQL 语句拆分成最小单元(如关键字SELECT、表名、字段名、条件等),识别每个单元的含义

语法分析:校验 SQL 语法是否符合 MySQL 规范,若语法错误会直接返回报错(如You have an error in your SQL syntax);语法正确则生成抽象语法树(AST)

  • 优化器(Optimizer)

MySQL 的 “智能决策层”,核心是选择最优执行计划:例如:多表 JOIN 时选择哪个表作为驱动表、WHERE 条件中选择哪个索引、是否使用全表扫描 / 索引扫描等。优化器仅负责"选择计划",不执行SQL,执行计划可通过EXPLAIN命令查看

MySQL执行计划详解:从看不懂到秒懂,一线DBA的实战笔记

  • 执行器(Executor)

真正执行 SQL 的核心组件:首先校验该用户是否有操作目标表 / 字段的权限(若无则返回权限错误);然后调用对应存储引擎的 API(如 InnoDB 的read_row/write_row),而非直接操作数据;最后整理执行结果(如分页、排序)并返回给客户端

  • 存储引擎层(Storage Engines)

MySQL的“数据存储引擎”,是可插拔的架构(默认 InnoDB),负责数据的实际读写、事务管理、锁机制、索引维护等

  • 磁盘层

存储MySQL的持久化数据,包括:数据文件(.ibd/.MYD)、日志文件(binlog/redo log/undo log)、配置文件等,存储引擎直接与磁盘交互完成数据读写

三、 结语

SQL执行顺序是MySQL核心原理之一,深入理解它有助于我们编写出更高效、更可靠的查询语句。下次当你编写复杂SQL时,不妨在脑海中过一遍MySQL的执行流程,相信你会更加得心应手!

希望本文对你理解MySQL的SQL执行顺序有所帮助。如果你有更好的技巧或疑问,欢迎留言讨论!

关注微信公众号「数据库干货铺」,获取更多数据库运维干货。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-01-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据库干货铺 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档