SQL执行顺序为FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY→LIMIT,WHERE只能用原始列,HAVING用于过滤分组结果,SELECT别名在同级WHERE/GROUP BY中不可用,GROUP BY后非聚合列必须出现在GROUP BY中。

SELECT 执行顺序不是从左到右写的顺序
SQL 语句里 SELECT a, b FROM t WHERE c > 1 GROUP BY a 看起来是“先选字段,再筛数据”,但实际执行时,WHERE 一定比 SELECT 先跑,GROUP BY 又在 WHERE 之后、SELECT 之前。这个顺序决定了你能不能在某一步用某个字段或表达式。
常见错误现象:SELECT name, COUNT(*) FROM users WHERE COUNT(*) > 5 直接报错,因为 COUNT(*) 是聚合结果,还没算出来,WHERE 阶段根本访问不到;得换 HAVING。
-
FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY -
WHERE只能用原始列(如age),不能用别名或聚合函数 -
HAVING可以用聚合函数和SELECT中定义的别名(部分数据库支持,但别依赖) -
SELECT里写的别名,在同级的WHERE或GROUP BY中不可用(MySQL 8.0+ 在GROUP BY中允许用SELECT别名,但 PostgreSQL 不行,别写)
WHERE 和 HAVING 的分工必须分清
WHERE 是“查之前过滤”,HAVING 是“分完组再筛组”。混淆这两者,轻则查不出数据,重则逻辑全错。
使用场景:比如要找“订单总额超 1000 的用户”,得先按用户分组,再筛组——这是 HAVING;但“只看 2023 年的订单”,年份是单条记录属性,应该在 WHERE 里提前过滤,减少后续分组的数据量。
-
WHERE过滤行,作用于未分组前的每一行,能用索引加速 -
HAVING过滤组,作用于GROUP BY后的聚合结果,通常无法走索引 - 错误示例:
SELECT user_id, SUM(amount) s FROM orders GROUP BY user_id HAVING created_at > '2023-01-01'——created_at不在GROUP BY里,也不是聚合值,语法直接报错(PostgreSQL 报column "orders.created_at" must appear in the GROUP BY clause)
GROUP BY 后 SELECT 列必须合法
标准 SQL 要求:SELECT 列要么是 GROUP BY 中的列,要么是聚合函数结果。MySQL 默认宽松(sql_mode 不含 ONLY_FULL_GROUP_BY),但其他数据库(PostgreSQL、SQL Server)严格报错。
容易踩的坑:本地开发用 MySQL 没报错,上线到生产环境(可能是 PostgreSQL 或开启了严格模式的 MySQL)直接失败。
- 正确写法:
SELECT dept, AVG(salary) FROM emp GROUP BY dept - 危险写法:
SELECT dept, name, AVG(salary) FROM emp GROUP BY dept——name未聚合也未分组,结果不确定(MySQL 返回任意一行的name,PostgreSQL 直接拒掉) - 如果真要带非分组字段,明确用聚合函数包裹,比如
MAX(name)或STRING_AGG(name, ',')(PostgreSQL) - MySQL 8.0+ 支持函数依赖推导,但别指望它帮你兜底;写 SQL 时就按标准来最稳妥
ORDER BY 和 LIMIT 的位置影响性能和语义
ORDER BY 在 SELECT 之后执行,所以可以用 SELECT 中的别名或表达式;但 LIMIT(或 TOP、FETCH FIRST)是最后一步,它截的是最终排序后的结果集。
性能影响明显:如果 WHERE 条件没压住数据量,GROUP BY 后仍有几万组,再 ORDER BY ... LIMIT 10,数据库仍得算完整个分组结果才排序取头十条——很慢。
- 优化思路:尽量让
WHERE提前过滤,或加覆盖索引(比如(status, created_at)支持WHERE status = 1 ORDER BY created_at DESC LIMIT 10) -
ORDER BY中用GROUP BY字段安全;用聚合字段也 OK,比如ORDER BY COUNT(*) DESC - MySQL 用
LIMIT,PostgreSQL 用LIMIT/OFFSET或FETCH FIRST 10 ROWS ONLY,SQL Server 用TOP或OFFSET-FETCH,语法不通用,别硬套
执行顺序不是语法顺序,而是引擎真正干活的链条。写的时候盯着这根链,每一步能用什么、不能用什么,边界就清楚了。最常翻车的地方,其实是把 HAVING 当 WHERE 用,或者在 GROUP BY 后 SELECT 了不该出现的列——不是数据库不讲道理,是你没对上它的阶段感。










