SQL 语句的执行顺序和书写顺序并不一致。数据库引擎的逻辑处理顺序是 FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT。掌握这个顺序,能直接避开”在 WHERE 里用聚合函数”这类报错,也能让查询更早过滤、跑得更快。本文更新于 2026年8月,引用资料截至该时间点。
一、书写顺序与执行顺序为何不同
我们写 SELECT 查询时,习惯把 SELECT 放在开头、FROM 紧随其后。但引擎读一条语句时,必须先知道”从哪张表取数”,才能谈”要哪些列”。执行顺序服务于数据的实际加工路径,而不是语法书写习惯。
下面把常见子句的两种顺序并排,差异一目了然:
| 子句 | 书写时常见位置 | 逻辑执行顺序 | 说明 |
|---|---|---|---|
| SELECT | 第 1 位 | 第 5~6 位 | 最后才计算输出列与别名 |
| FROM | 第 2 位 | 第 1 位 | 先锁定数据来源与 JOIN |
| WHERE | 第 3 位 | 第 2 位 | 分组前过滤行 |
| GROUP BY | 第 4 位 | 第 3 位 | 按列归组 |
| HAVING | 第 5 位 | 第 4 位 | 分组后过滤组 |
| ORDER BY | 第 6 位 | 第 7 位 | 排序可用 SELECT 别名 |
| LIMIT | 末尾 | 末尾 | 截断结果集 |
SQL Server 官方文档给出的完整序列更长,包含 ON、JOIN、WITH CUBE/ROLLUP、DISTINCT、TOP 等步骤(FROM → ON → JOIN → WHERE → GROUP BY → WITH CUBE/ROLLUP → HAVING → SELECT → DISTINCT → ORDER BY → TOP),核心骨架与上述一致(来源:Microsoft SQL Server 文档执行序列说明,经 Stack Overflow 引用)。
二、逻辑执行顺序逐步拆解
- FROM 与 JOIN:引擎先确定参与查询的表,执行 JOIN 合并行,生成待处理的工作集。这一步决定了后续所有操作的数据体量,是性能的第一道关口。
- WHERE:对合并后的每一行做条件过滤,不满足的行被丢弃。此时聚合结果还不存在,所以 WHERE 不能引用
SUM()、COUNT()这类聚合。 - GROUP BY:把 WHERE 后剩余的行按指定列归并成组,唯一值有多少,组就有多少。
- HAVING:对分组结果再做过滤,专管聚合条件(如
HAVING SUM(amount) > 1000)。 - SELECT:此时才计算最终要输出的列、表达式和别名。
- DISTINCT:剔除 SELECT 结果中的重复行。
- ORDER BY:对结果排序,因 SELECT 已算完,这里可以引用列别名。
- LIMIT / OFFSET:最后截断,只返回指定范围的行。
2.1 FROM 与 JOIN:先定来源
大表 JOIN 大表极易吃内存。GeeksforGeeks 在讲解执行顺序时强调,FROM 是处理起点,JOIN 是第一步实际发生的事(来源:GeeksforGeeks《Order of Execution of SQL Queries》)。实践上,先把表缩小再 JOIN 更稳妥。
2.2 WHERE:分组前过滤行
WHERE 的过滤发生在 GROUP BY 之前,因此它能大幅缩减进入聚合的数据量。试图在 WHERE 里写 WHERE SUM(area) > 1000 会直接报错,因为聚合值在该阶段尚未算出(来源:Sisense SQL order of operations 一文举例)。
2.3 GROUP BY 与 HAVING:分组后过滤组
GROUP BY 折叠出唯一组,HAVING 再对组设门槛。两者分工清晰:行级条件进 WHERE,组级条件进 HAVING。
2.4 SELECT:最后才计算输出列
别名在 SELECT 阶段才诞生。这解释了为什么 WHERE 里用不了别名,而 ORDER BY 可以——它们一个在前、一个在后。
2.5 ORDER BY 与 LIMIT:排序与截断
排序是资源消耗大户,放在流程末尾,只对已经缩小的数据集生效,是执行顺序对性能的隐性保护。
三、为什么要了解执行顺序
理解执行顺序,价值体现在四处。
少写报错查询。 知道 WHERE 早于聚合,就不会把 SUM() 塞进 WHERE;知道别名晚于 SELECT,就不会在 WHERE 引用刚起的别名。SQLbolt 将”知道结果在各阶段何处可用”列为理解执行顺序的核心原因(来源:SQLbolt《Lesson 12》)。
写出更快的查询。 执行顺序决定了”先过滤、后聚合、再排序”。把能过滤的条件尽量前置到 WHERE,让进入 GROUP BY 和 ORDER BY 的数据更少,整体开销随之下降。
看懂执行计划。 引擎给出的执行计划(EXPLAIN)本质是对这条逻辑顺序的物理实现。理解顺序,才能读懂计划里”先扫描哪张表、在哪一步 JOIN、何时排序”。
理解聚合的作用域。 明确聚合函数算在哪一步,才能正确区分行级过滤与组级过滤,避免统计口径错误。
四、逻辑顺序不等于物理执行
需要区分”逻辑处理顺序”和”物理执行顺序”。逻辑顺序是 SQL 标准约定的理解框架,保证结果正确;物理执行由查询优化器决定,可调整步骤以提速,但必须返回与逻辑顺序相同的结果(来源:GeeksforGeeks《SQL Engine》)。
华为云数据仓库服务(DWS)的官方文档将 SQL 引擎处理一条查询类语句划分为五个阶段:语法&词法解析、语义解析、查询重写、查询优化、查询执行(据华为云文档《SQL查询执行流程》,更新于 2026-03-25)。其中查询优化阶段依赖表统计信息,由基于代价的优化器(CBO)估算每种执行方式的代价后选出计划。SQL Server 数据库引擎同样采用 CBO,依据表元组数、字段宽度、NULL 比率、distinct 值等统计信息为 JOIN 方式(Nested Loop、Merge Join、Hash Join)选型(据 SQL Server 文档)。这些都属于引擎层面的客观实现,不改变本章所述逻辑顺序。
图1(概念流程):SQL 逻辑执行顺序——FROM/JOIN → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT。该流程图用于建立心智模型,物理执行由优化器在此基础上重组。
五、实战编写检查清单
按执行顺序组织思路,写查询时照此走一遍:
- 先列 FROM 与必要的 JOIN,确认数据来源正确。
- 把所有行级过滤写进 WHERE,尽量前置、利用索引。
- 只在需要汇总时加 GROUP BY,并在 HAVING 放组级条件。
- SELECT 只取需要的列,避免
SELECT *。 - 用 ORDER BY 控制排序,配合 LIMIT 做分页或取 Top N。
- 用 EXPLAIN 核对实际执行计划,验证过滤是否提前生效。
常见问题(FAQ)
Q1:WHERE 和 HAVING 有什么区别?
A1:WHERE 在分组前过滤行,HAVING 在分组后过滤组;聚合条件只能放 HAVING,行级条件放 WHERE。
Q2:为什么 WHERE 里用不了 SELECT 别名?
A2:WHERE 执行早于 SELECT,别名在 SELECT 阶段才生成;ORDER BY 在 SELECT 之后,因此可用别名。
Q3:数据库一定按这个顺序跑吗?
A3:逻辑顺序是理解规则用的框架,物理执行由优化器调整,但结果与逻辑顺序等价。
参考来源
- SQLbolt,《Lesson 12: Order of execution of a Query》
- GeeksforGeeks,《Order of Execution of SQL Queries》《SQL Engine》
- 华为云文档,《SQL查询执行流程》(更新于 2026-03-25)
- Microsoft SQL Server 文档执行序列说明(经 Stack Overflow 引用)
- Sisense,《SQL query order of operations》