这是一个非常经典且能体现“资深感”的问题。在编写 SQL 时,我们是按 SELECT ... FROM ... WHERE 的顺序写的,但 MySQL 引擎在执行时,为了保证数据过滤的逻辑准确性,有着一套完全不同的**“逻辑执行顺序”**。
理解这个顺序,能帮你瞬间查出为什么 GROUP BY 里的别名不能用在 WHERE 里,或者为什么 HAVING 比 WHERE 慢。
🚀 MySQL 逻辑执行顺序表
按照从先到后的执行逻辑,顺序如下:
| 序号 | 关键字 | 执行动作 | 形象理解 |
| 1 | FROM | 锁定数据表(如果是多表则进行笛卡尔积/连接) | 进仓库,找到装货的架子 |
| 2 | ON | 应用连接条件 | 筛选出符合匹配关系的零件 |
| 3 | JOIN | 合并表数据 | 把两个架子的零件组装起来 |
| 4 | WHERE | 阶段性过滤(此时不能用 SELECT 里的别名) | 扔掉不合格的零件 |
| 5 | GROUP BY | 分组汇总 | 把剩下的零件按颜色分堆 |
| 6 | WITH ROLLUP / CUBE | 分组增强(MySQL 特有) | 给每堆零件算个小计 |
| 7 | HAVING | 对分组后的结果再过滤 | 扔掉数量太少的那几堆 |
| 8 | SELECT | 确定要展示的列(执行函数、计算别名) | 把零件装进展示盒,贴上标签 |
| 9 | DISTINCT | 去重 | 拿走盒子里重复的样板 |
| 10 | ORDER BY | 排序 | 按价格高低摆放展示盒 |
| 11 | LIMIT / OFFSET | 截取最终结果 | 只拿前 5 盒给客户看 |
🔍 三个关键知识点(面试避坑指南)
1. 为什么 WHERE 里不能用 SELECT 定义的别名?
-
原因:通过上面的表你可以看到,
WHERE(第 4 步)的执行早于SELECT(第 8 步)。 -
现象:如果你写
SELECT age AS user_age ... WHERE user_age > 18,MySQL 会报错说找不到user_age,因为它还没走到给列起名字的那一步。
2. WHERE vs. HAVING 的本质区别
-
WHERE 是在“分堆”之前,直接对着原始行数据开刀。它能利用索引,速度极快。
-
HAVING 是在“分堆”并计算好聚合函数(如
COUNT,AVG)之后,对着结果集过滤。它通常无法利用索引。 -
资深建议:能写在
WHERE里的过滤条件,绝对不要写在HAVING里。
3. SELECT 其实发生得非常晚
很多人以为 SELECT 是第一步,其实它排在第 8 位。在它之前,所有的过滤、连接、分组都已经尘埃落定了。SELECT 只是最后把数据取出来,并做一些 1 + 1 = 2 或者 UPPER(name) 这样的表面格式处理。
💡 深度关联:这和 MVCC 有关系吗?
有!当你执行到第一步 FROM 和 WHERE 时:
-
如果是快照读(普通 SELECT),MySQL 会在这一刻确定它的 ReadView(快照照片)。
-
如果是当前读(FOR UPDATE),MySQL 会在这一刻根据索引条件,在对应的行上加上 Next-Key Lock。
📉 状态同步
-
2026-03-25:已梳理 MySQL 逻辑执行顺序及其与别名、索引过滤的深层关系。
掌握了这个顺序,你写 SQL 的思路会像看代码执行路径一样清晰。
既然聊到了执行顺序,你想看看如何通过 EXPLAIN 命令来观察 MySQL 实际上是怎么优化这个执行计划(Execution Plan)的吗?