跳转到内容

一条SQL的执行顺序

💡

很多开发者按照自己"写SQL的顺序"理解SQL执行,但SQL的书写顺序和执行顺序完全不同。理解SQL真实的执行顺序,是掌握SQL优化、避免常见错误的关键。


一、核心口诀

从哪连什么,分组再筛选,选择去重排,最后限数量

#

关键字

口诀

作用

1

FROM

从哪

确定数据来源表,加载数据到内存

2

JOIN

连什么

关联其他表,生成笛卡尔积

3

ON

(连接条件)

筛选连接条件,过滤无效连接行

4

WHERE

什么

对原始数据进行行级过滤

5

GROUP BY

分组

将过滤后的数据按指定列分组

6

HAVING

再筛选

对分组后的结果再次过滤

7

SELECT

选择

选取需要的列(此时才计算SELECT中的表达式和别名)

8

DISTINCT

去重

对结果集去重

9

ORDER BY

对结果集排序(此时才可以使用SELECT中的别名)

10

LIMIT

最后限数量

截取指定行数返回


二、书写顺序 vs 执行顺序

2.1 你写的SQL顺序

2.2 实际执行顺序

最关键的认知:WHERE在GROUP BY之前执行,HAVING在GROUP BY之后执行。这就是为什么WHERE不能用聚合函数(此时还没分组计算),HAVING可以用聚合函数(此时已分组计算完毕)。


三、逐步详解

3.1 第一步:FROM — 确定数据来源

  • 首先确定查询针对哪个表,数据库定位到该表的数据页
  • 如有子查询,先执行子查询生成临时表,再作为FROM表

3.2 第二步:JOIN + ON — 表关联

  • FROM表 与 JOIN表 做笛卡尔积(所有行的组合)
  • ON条件筛选出真正匹配的行,不匹配的行不参与后续计算
  • 注意:LEFT JOIN时,左表不匹配的行会被保留(右表字段填充NULL)

3.3 第三步:WHERE — 行级过滤

💡

WHERE阶段不能使用SELECT中定义的别名!SELECT在WHERE之后执行,别名还不存在。同理,WHERE中不能使用聚合函数(COUNT/SUM/AVG等),因为此时尚未执行GROUP BY。

3.4 第四步:GROUP BY — 分组

  • 将WHERE过滤后的数据按指定列分组
  • 每个组会折叠成一行
  • 未出现在GROUP BY中的非聚合列,SELECT不能直接引用(ONLY_FULL_GROUP_BY模式报错)

3.5 第五步:HAVING — 分组后过滤

  • 对分组后的每个组进行过滤
  • 可以使用聚合函数:HAVING COUNT(*) > 5 合法(此时已分组计算完毕)
  • 性能建议:能用WHERE过滤的尽量用WHERE(在分组前缩小数据量);只有必须依赖聚合结果的才用HAVING

3.6 第六步:SELECT — 选取列

  • 此时才选取需要输出的列,计算SELECT中的表达式和函数
  • SELECT中定义的别名,在SELECT之后的ORDER BY/LIMIT中可以使用
  • 聚合函数(SUM/COUNT/AVG/MAX/MIN)在这一步计算

3.7 第七步:DISTINCT — 去重

  • 对SELECT选取的所有列组合进行去重
  • DISTINCT作用于所有SELECT出的列,不能只对部分列去重

3.8 第八步:ORDER BY — 排序

  • 对结果集进行排序
  • 可以使用SELECT中定义的别名(因为SELECT已经执行)
  • 排序可能需要filesort(文件排序),是常见性能瓶颈

3.9 第九步:LIMIT — 截取行数

  • 在整个流程的最后,截取指定数量的行返回给客户端
  • LIMIT m, n:跳过m行取n行(深度分页的根源)

四、实际案例演示

4.1 完整示例

4.2 数据流向模拟

💡

可以看到数据量逐级减少——这就是为什么WHERE过滤越早越好,它决定了后面所有步骤要处理的数据量。


五、常见误区

误区

真相

WHERE中用了SELECT的别名

❌ 报错!SELECT在WHERE之后,别名还不存在

HAVING和WHERE可以互换

❌ WHERE在分组前,HAVING在分组后。WHERE中不能用聚合函数

ORDER BY中不能用别名

✅ 可以用!ORDER BY在SELECT之后,别名已存在

SELECT语句先执行

❌ SELECT是倒数第四步执行


记忆口诀再读一遍:从哪连什么,分组再筛选,选择去重排,最后限数量。理解了这个顺序,你对SQL的执行过程就有了清晰的心智模型。