MySQL 中 SELECT 语句的逻辑执行顺序
(非常重要且经常被问到的高频知识点)
MySQL 的 SELECT 语句书写顺序和实际执行顺序是完全不同的,这是很多人在理解 SQL 执行过程时最容易混淆的地方。
书写顺序(我们平时怎么写的)
SELECT ...,
...
FROM ...
JOIN ...
WHERE ...
GROUP BY ...
HAVING ...
ORDER BY ...
LIMIT ...
实际逻辑执行顺序(MySQL 真正处理的顺序)
| 顺序 | 关键字/子句 | 说明 | 是否能使用前面产生的别名 | 执行次数 |
|---|---|---|---|---|
| 1 | FROM | 确定要查询的表/视图,并进行笛卡尔积(如果多表) | — | 1 |
| 2 | JOIN / ON | 执行各种 JOIN(LEFT/RIGHT/INNER/CROSS 等) | — | 1 |
| 3 | WHERE | 对连接后的结果集进行过滤(最早能做过滤的位置) | 不能使用 SELECT 中的别名 | 1 |
| 4 | GROUP BY | 按指定字段分组(分组前会先做聚合前的排序优化) | 不能使用 SELECT 中的别名 | 1 |
| 5 | 聚合函数(COUNT/SUM等) | 在分组的基础上计算聚合值 | — | 按分组次数 |
| 6 | HAVING | 对分组后的结果进行过滤(是唯一可以直接使用聚合函数过滤的地方) | 可以使用 SELECT 中的别名 | 1 |
| 7 | SELECT | 最终决定要返回哪些列,可以使用表达式、别名、聚合函数等 | — | 1 |
| 8 | DISTINCT | 去重(如果有的话) | — | 1 |
| 9 | ORDER BY | 对最终结果集排序 | 可以使用 SELECT 中的别名 | 1 |
| 10 | LIMIT / OFFSET | 分页限制 | — | 1 |
简洁记忆口诀(推荐背诵)
“从哪来 → 连哪去 → 筛哪群 → 聚哪算 → 再筛哪 → 选哪列 → 排哪序 → 取哪几”
更短的版本(7个字):
从连筛聚算选排取
常见的“为什么报错”与“为什么结果不对”的典型例子
-- 错误写法1:WHERE 里不能用别名
SELECT age/12 AS age_year
FROM users
WHERE age_year > 3; -- 报错:Unknown column 'age_year'
-- 正确写法1
SELECT age/12 AS age_year
FROM users
HAVING age_year > 3; -- 可以(HAVING 能识别别名)
-- 或使用子查询/派生表
SELECT * FROM (
SELECT age/12 AS age_year
FROM users
) t
WHERE t.age_year > 3;
-- 错误写法2:ORDER BY 里能用别名,但 WHERE 不行
SELECT id, COUNT(*) AS cnt
FROM orders
GROUP BY id
ORDER BY cnt DESC; -- 正确
-- 但这样不行:
WHERE cnt > 10 -- 错误
HAVING cnt > 10 -- 正确
快速对照表(面试/写 SQL 必备)
| 想在这位置使用别名? | 能用别名? | 能用聚合函数? | 典型用法场景 |
|---|---|---|---|
| WHERE | × | × | 原始数据过滤 |
| GROUP BY | × | × | 分组字段 |
| HAVING | ○(MySQL特有) | ○ | 分组后过滤 |
| SELECT | —(自己定义) | ○ | 决定输出列 |
| ORDER BY | ○ | ○ | 排序(最常用别名的地方) |
| LIMIT | — | — | 分页 |
2024-2025 年常考变种问题
- WHERE 和 HAVING 区别(背诵经典回答)
- GROUP BY + ORDER BY 的隐含排序(MySQL 5.7 之前默认按 GROUP BY 排序,8.0 之后取消了)
- 窗口函数(OVER)是在哪个阶段执行的?(SELECT 阶段之后,但 ORDER BY 之前)
- 同一个查询里 HAVING 能用别名,WHERE 为什么不行?(因为执行顺序不同)
一句话总结(可以直接用来回答面试):
MySQL SELECT 语句的逻辑执行顺序是:
FROM → JOIN → WHERE → GROUP BY → 聚合 → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT
这个顺序几乎决定了你在 SQL 里能写什么、不能写什么、写在哪里。
需要我继续讲更深入的执行细节吗?
比如:
- 实际物理执行计划可能怎么优化这个逻辑顺序
- 窗口函数、子查询、CTE 分别在哪个阶段介入
- 为什么很多 DBA 更喜欢把过滤条件写在 FROM 子句的 JOIN ON 里而不是 WHERE
告诉我你想往哪个方向继续深入~