面试频率:★★★★☆
工作频率:★★★★★
⚡ 30 秒速记
SQL 的书写顺序:
SELECT
FROM
JOIN
ON
WHERE
GROUP BY
HAVING
ORDER BY
LIMIT
但是 SQL 的逻辑执行顺序大致是:
FROM
↓
JOIN
↓
ON
↓
WHERE
↓
GROUP BY
↓
HAVING
↓
SELECT
↓
DISTINCT
↓
ORDER BY
↓
LIMIT
一句话记忆:
找表
↓
连表
↓
匹配
↓
过滤行
↓
分组
↓
过滤组
↓
选字段
↓
去重
↓
排序
↓
分页
最重要的三个知识点:
WHERE
=
GROUP BY之前过滤行
HAVING
=
GROUP BY之后过滤组
ORDER BY
=
逻辑上在SELECT之后
所以通常可以使用SELECT别名
注意:
这是 SQL 的逻辑执行顺序,不代表 MySQL 内部真正严格按照这个顺序物理执行。实际执行方式由优化器决定,可以通过 EXPLAIN 查看执行计划。
🎤 面试回答
面试官:
SQL 的执行顺序是什么?
可以回答:
SQL 的逻辑执行顺序大致是 FROM、JOIN、ON、WHERE、GROUP BY、HAVING、SELECT、DISTINCT、ORDER BY、LIMIT。
FROM、JOIN 和 ON 先确定数据来源以及表之间的关联关系,WHERE 在分组之前过滤数据行,然后 GROUP BY 进行分组,HAVING 对分组后的结果进行过滤。
接着 SELECT 选择需要返回的字段,DISTINCT 进行去重,ORDER BY 对结果进行排序,最后 LIMIT 限制返回的数据量。
需要注意,这只是 SQL 的逻辑处理顺序,并不代表 MySQL 实际严格按照这个顺序物理执行。MySQL 优化器会根据索引、统计信息和成本生成执行计划,实际执行情况可以通过 EXPLAIN 分析。
一、先看一个完整 SQL
例如:
SELECT department, COUNT(*) AS cnt
FROM employee e
JOIN company c
ON e.company_id = c.id
WHERE e.age > 20
GROUP BY department
HAVING COUNT(*) > 5
ORDER BY cnt DESC
LIMIT 10;
SQL 写出来是:
SELECT
↓
FROM
↓
JOIN
↓
ON
↓
WHERE
↓
GROUP BY
↓
HAVING
↓
ORDER BY
↓
LIMIT
但是逻辑上可以理解为:
FROM employee
↓
JOIN company
↓
ON e.company_id = c.id
↓
WHERE e.age > 20
↓
GROUP BY department
↓
HAVING COUNT(*) > 5
↓
SELECT department, COUNT(*) AS cnt
↓
ORDER BY cnt DESC
↓
LIMIT 10
二、FROM
第一步:
FROM employee
作用:
确定数据来源
可以理解:
我要查询数据
↓
先确定从哪里获取数据
所以:
FROM
=
确定数据来源
三、JOIN
如果查询涉及多张表:
FROM employee e
JOIN company c
就需要:
JOIN
把表关联起来。
例如:
employee
↓
JOIN
↓
company
但是:
两张表根据什么关联?
这就需要:
ON
四、ON
例如:
ON e.company_id = c.id
作用:
确定JOIN匹配条件
例如:
employee
张三 company_id = 1
李四 company_id = 2
company:
id name
1 公司A
2 公司B
根据:
e.company_id = c.id
匹配:
张三
company_id = 1
↓
company.id = 1
↓
公司A
所以:
FROM
↓
JOIN
↓
ON
确定:
数据来源
+
关联关系
五、WHERE
接下来:
WHERE e.age > 20
作用:
过滤数据行
例如:
张三 18岁 ❌
李四 20岁 ❌
王五 21岁 ✅
赵六 25岁 ✅
所以:
WHERE
=
过滤行
最重要的是:
WHERE
↓
GROUP BY
也就是:
WHERE 在 GROUP BY 之前过滤数据。
六、GROUP BY
经过 WHERE:
张三 技术部
李四 技术部
王五 销售部
赵六 技术部
小明 销售部
执行:
GROUP BY department
按照部门进行分组:
技术部
├── 张三
├── 李四
└── 赵六
销售部
├── 王五
└── 小明
所以:
GROUP BY
=
分组
完整过程:
原始数据
↓
WHERE
↓
过滤不需要的数据
↓
GROUP BY
↓
对剩余数据进行分组
七、HAVING
GROUP BY 以后:
技术部 → 10人
销售部 → 8人
财务部 → 3人
执行:
HAVING COUNT(*) > 5
得到:
技术部 → 10人 ✅
销售部 → 8人 ✅
财务部 → 3人 ❌
所以:
HAVING
=
过滤分组后的结果
八、WHERE 和 HAVING 的区别
这是最经典的面试题之一。
WHERE
GROUP BY之前
↓
过滤数据行
例如:
WHERE age > 20
过滤的是:
员工
HAVING
GROUP BY之后
↓
过滤分组结果
例如:
HAVING COUNT(*) > 5
过滤的是:
部门分组
所以:
WHERE
↓
过滤行
GROUP BY
↓
分组
HAVING
↓
过滤组
一句话:
WHERE 是分组前过滤行,HAVING 是分组后过滤组。
九、为什么聚合函数过滤经常使用 HAVING?
例如:
SELECT department, COUNT(*)
FROM employee
GROUP BY department
HAVING COUNT(*) > 5;
因为:
COUNT(*)
是对:
分组结果
进行统计。
逻辑:
employee
↓
GROUP BY department
↓
形成部门分组
↓
COUNT(*)
↓
统计每组数量
↓
HAVING COUNT(*) > 5
↓
过滤分组
所以:
针对聚合结果过滤
通常使用:
HAVING
十、SELECT
经过:
FROM
JOIN
ON
WHERE
GROUP BY
HAVING
之后,逻辑上再处理:
SELECT department, COUNT(*) AS cnt
也就是:
选择最终需要返回的字段 / 表达式
例如:
department cnt
技术部 10
销售部 8
十一、为什么 WHERE 通常不能直接使用 SELECT 别名?
例如:
SELECT age AS a
FROM employee
WHERE a > 20;
为什么不能想当然这么写?
看逻辑顺序:
WHERE
↓
SELECT
也就是说:
WHERE逻辑处理的时候
SELECT:
age AS a
这个别名逻辑上还没有产生。
可以理解:
WHERE a > 20
↓
a是谁?
↓
还没有SELECT
SELECT age AS a
↓
这里才产生a
所以应该:
SELECT age AS a
FROM employee
WHERE age > 20;
注意:
不同数据库对别名的具体语法支持可能存在差异,但用逻辑执行顺序理解最清楚。
十二、DISTINCT
例如:
SELECT DISTINCT department
FROM employee;
可以理解:
SELECT department
↓
得到结果
↓
DISTINCT
↓
去重
例如:
技术部
技术部
销售部
技术部
销售部
经过 DISTINCT:
技术部
销售部
所以:
DISTINCT
=
结果去重
十三、ORDER BY
然后:
ORDER BY cnt DESC
作用:
排序
例如:
技术部 10
销售部 8
研发部 20
执行:
ORDER BY cnt DESC
得到:
研发部 20
技术部 10
销售部 8
十四、为什么 ORDER BY 可以使用 SELECT 别名?
例如:
SELECT salary * 12 AS year_salary
FROM employee
ORDER BY year_salary DESC;
逻辑顺序:
SELECT
↓
产生year_salary
↓
ORDER BY
↓
使用year_salary排序
所以:
ORDER BY
通常可以使用:
SELECT别名
十五、WHERE 和 ORDER BY 使用别名的区别
假设:
SELECT salary * 12 AS year_salary
FROM employee;
逻辑:
WHERE
↓
SELECT
↓
ORDER BY
所以:
WHERE
↓
SELECT之前
↓
通常不能使用SELECT别名
而:
ORDER BY
↓
SELECT之后
↓
通常可以使用SELECT别名
一张图:
WHERE
↓
SELECT
↓
产生 year_salary
↓
ORDER BY
↓
可以使用 year_salary
十六、LIMIT
最后:
LIMIT 10
作用:
限制最终返回的数据量
例如:
SELECT *
FROM employee
ORDER BY salary DESC
LIMIT 10;
逻辑:
employee
↓
形成结果
↓
按照salary降序
↓
LIMIT 10
↓
返回前10条
也就是:
工资最高的10条记录
十七、ORDER BY 和 LIMIT 的关系
例如:
SELECT *
FROM orders
ORDER BY create_time DESC
LIMIT 20;
逻辑:
订单数据
↓
ORDER BY create_time DESC
↓
最新数据排在前面
↓
LIMIT 20
↓
取前20条
所以:
ORDER BY
↓
LIMIT
逻辑上:
先排序,再限制返回数量。
这在:
分页
Top N查询
最新数据查询
中非常常见。
十八、和深分页串起来
之前学过:
SELECT *
FROM orders
ORDER BY id
LIMIT 5000000, 10;
逻辑上:
FROM orders
↓
得到满足条件的数据
↓
ORDER BY id
↓
按照id排序
↓
LIMIT 5000000,10
↓
跳过大量前置记录
↓
返回10条
所以:
LIMIT
逻辑上位于:
ORDER BY
之后。
也能帮助理解为什么:
大OFFSET
可能带来较高成本。
十九、完整案例执行流程
SQL:
SELECT department, COUNT(*) AS cnt
FROM employee e
JOIN company c
ON e.company_id = c.id
WHERE e.age > 20
GROUP BY department
HAVING COUNT(*) > 5
ORDER BY cnt DESC
LIMIT 10;
完整逻辑:
FROM employee
↓
确定employee数据源
↓
JOIN company
↓
关联company
↓
ON e.company_id = c.id
↓
确定关联条件
↓
WHERE e.age > 20
↓
过滤20岁及以下员工
↓
GROUP BY department
↓
按照部门分组
↓
HAVING COUNT(*) > 5
↓
只保留人数大于5的部门
↓
SELECT department, COUNT(*) AS cnt
↓
选择部门和人数
↓
ORDER BY cnt DESC
↓
按照人数降序
↓
LIMIT 10
↓
返回前10条
二十、一张图彻底记住
SQL
↓
FROM employee
↓
确定数据源
↓
JOIN company
↓
ON company_id = id
↓
完成表关联
↓
WHERE age > 20
↓
过滤行
↓
GROUP BY department
↓
分组
↓
HAVING COUNT(*) > 5
↓
过滤组
↓
SELECT department, COUNT(*) AS cnt
↓
选择返回字段
↓
DISTINCT
↓
去重
↓
ORDER BY cnt DESC
↓
排序
↓
LIMIT 10
↓
最终结果
🎯 面试追问
Q1:WHERE 和 HAVING 有什么区别?
答:
WHERE
↓
GROUP BY之前
↓
过滤数据行
HAVING
↓
GROUP BY之后
↓
过滤分组结果
一句话:
WHERE 过滤行,HAVING 过滤组。
Q2:为什么聚合条件通常写 HAVING?
例如:
HAVING COUNT(*) > 5
因为:
GROUP BY
↓
形成分组
↓
产生聚合结果
↓
HAVING
↓
过滤聚合结果
Q3:为什么 WHERE 通常不能直接使用 SELECT 别名?
因为逻辑顺序:
WHERE
↓
SELECT
WHERE 处理时:
SELECT别名逻辑上还没有产生
Q4:为什么 ORDER BY 通常可以使用 SELECT 别名?
因为:
SELECT
↓
产生别名
↓
ORDER BY
所以:
ORDER BY
通常可以使用 SELECT 别名。
Q5:ORDER BY 和 LIMIT 谁先?
逻辑上:
ORDER BY
↓
LIMIT
也就是:
先排序
↓
再取数据
Q6:WHERE 和 GROUP BY 谁先?
WHERE
↓
GROUP BY
也就是:
先过滤
↓
再分组
Q7:GROUP BY 和 HAVING 谁先?
GROUP BY
↓
HAVING
也就是:
先分组
↓
再过滤组
Q8:SELECT 和 ORDER BY 谁先?
逻辑上:
SELECT
↓
ORDER BY
这也是 ORDER BY 通常可以使用 SELECT 别名的原因之一。
Q9:LIMIT 为什么在最后?
因为:
前面的查询结果
↓
完成过滤 / 分组 / 排序等逻辑
↓
LIMIT
↓
限制最终返回数量
Q10:SQL 逻辑执行顺序就是 MySQL 真实执行顺序吗?
不是。
逻辑执行顺序
=
帮助理解SQL语义
真实执行:
SQL
↓
MySQL优化器
↓
成本估算
↓
生成执行计划
↓
执行
实际执行计划:
通过EXPLAIN分析
二十一、逻辑顺序和真实执行计划的区别
这里一定不要混淆。
我们学习:
FROM
↓
WHERE
↓
GROUP BY
↓
HAVING
↓
SELECT
↓
ORDER BY
↓
LIMIT
是为了理解:
SQL语义
例如:
为什么WHERE在分组前过滤?
为什么HAVING可以过滤COUNT结果?
为什么ORDER BY可以使用SELECT别名?
但是 MySQL 真正执行时:
不会机械地严格按照这张图
一步一步执行
因为 MySQL 有:
优化器
优化器会考虑:
索引
数据量
统计信息
JOIN顺序
覆盖索引
ICP
查询成本
然后:
生成执行计划
二十二、逻辑执行顺序和 EXPLAIN
可以这样区分:
SQL逻辑执行顺序
↓
理解SQL语义
例如:
WHERE
GROUP BY
HAVING
SELECT
ORDER BY
LIMIT
而:
EXPLAIN
↓
分析MySQL执行计划
重点看:
type
key
rows
Extra
所以:
逻辑执行顺序
=
理论上这条SQL表达什么意思
EXPLAIN
=
MySQL准备怎么把它执行出来
二十三、和之前的 MySQL 知识串起来
现在很多知识已经可以串起来:
FROM
↓
JOIN
↓
ON
↓
JOIN原理与索引优化
WHERE
↓
索引
↓
联合索引
↓
最左匹配
↓
索引失效
↓
ICP
GROUP BY
↓
GROUP BY索引优化
ORDER BY
↓
ORDER BY索引优化
LIMIT
↓
LIMIT深分页优化
最后:
EXPLAIN
↓
验证实际执行计划
整个体系:
SQL
↓
┌──────────┴──────────┐
↓ ↓
逻辑执行顺序 实际执行计划
↓ ↓
SQL语义 EXPLAIN
↓ ↓
WHERE / GROUP BY type / key / rows
HAVING / ORDER BY / Extra
LIMIT
⚠️ 易错点
1. SQL 书写顺序 ≠ SQL 逻辑执行顺序
书写:
SELECT
FROM
WHERE
GROUP BY
HAVING
ORDER BY
LIMIT
逻辑:
FROM
WHERE
GROUP BY
HAVING
SELECT
ORDER BY
LIMIT
不要混淆。
2. WHERE 和 HAVING 不能完全当成一样的东西
错误:
WHERE和HAVING都是过滤
↓
随便用
正确:
WHERE
=
分组前过滤行
HAVING
=
分组后过滤组
3. WHERE 通常不能直接使用 SELECT 别名
因为:
WHERE
↓
SELECT
SELECT 别名逻辑上:
还没产生
4. ORDER BY 通常可以使用 SELECT 别名
因为:
SELECT
↓
ORDER BY
5. LIMIT 逻辑上在 ORDER BY 后
例如:
ORDER BY salary DESC
LIMIT 10;
表示:
先按照工资降序
↓
再取前10条
6. 逻辑执行顺序 ≠ 物理执行顺序
不要在面试时说:
MySQL一定先把FROM所有数据全部查出来
然后WHERE
然后GROUP BY
然后SELECT
更准确:
FROM、WHERE、GROUP BY、HAVING、SELECT、ORDER BY、LIMIT 描述的是 SQL 的逻辑处理顺序,实际物理执行由 MySQL 优化器根据成本生成执行计划。
❓ 自测
-
SQL 的书写顺序是什么?
-
SQL 的逻辑执行顺序是什么?
-
FROM 的作用是什么?
-
JOIN 的作用是什么?
-
ON 的作用是什么?
-
WHERE 是过滤行还是过滤组?
-
WHERE 和 GROUP BY 谁先?
-
GROUP BY 的作用是什么?
-
HAVING 是过滤行还是过滤组?
-
GROUP BY 和 HAVING 谁先?
-
WHERE 和 HAVING 最大区别是什么?
-
为什么聚合函数过滤经常使用 HAVING?
-
SELECT 在逻辑执行顺序中的位置在哪里?
-
为什么 WHERE 通常不能直接使用 SELECT 别名?
-
DISTINCT 是干什么的?
-
ORDER BY 是干什么的?
-
为什么 ORDER BY 通常可以使用 SELECT 别名?
-
ORDER BY 和 LIMIT 谁先?
-
LIMIT 的作用是什么?
-
ORDER BY salary DESC LIMIT 10表示什么? -
为什么
WHERE age > 20在 GROUP BY 之前? -
HAVING COUNT(*) > 5过滤的是什么? -
SQL 逻辑执行顺序和 MySQL 真实执行顺序一样吗?
-
MySQL 实际执行 SQL 由谁决定?
-
EXPLAIN 是用来分析什么的?
-
SQL 逻辑执行顺序和 EXPLAIN 分别解决什么问题?
-
为什么说逻辑执行顺序可以帮助理解 SQL,而不能完全代表底层执行过程?
-
FROM、JOIN、ON 和你之前学习的 JOIN 优化有什么关系?
-
ORDER BY、LIMIT 分别和之前哪些优化知识有关?
-
请完整说出 SQL 的逻辑执行顺序。