写 SQL 这事,最容易被低估的就是这几个基础语法。前几天帮一个朋友排查线上报表慢的问题,表才十几万行,一条统计 SQL 却跑了十几秒,最后定位到原因很简单:他把客户维度的过滤条件写进了 HAVING,导致数据库先按所有客户分组、聚合,再回头筛掉大部分数据。如果当时 WHERE 先过滤,数据量至少减掉 80%,速度完全不一样。
这类问题几乎都出在 WHERE、GROUP BY、ORDER BY 的配合上。单独拿出来,每个语法看起来都不难,一旦组合在一起,很多人就开始犯迷糊:WHERE 能不能用聚合函数?GROUP BY 之后 SELECT 为什么报错?ORDER BY 的 DESC 为什么只对第一列生效?这篇就把这三个语法彻底串起来,从执行顺序讲到实操写法,再给一个完整的统计需求推演过程,看完你至少能应付 80% 的日常取数和报表场景。适合正在系统学 SQL、准备面试、以及每天都在写查询但偶尔会被基础问题卡住的同学。
1. 先搞懂 SQL 执行顺序:WHERE、GROUP BY、ORDER BY 为什么是这么排的
1.1 书写顺序不等于执行顺序
很多 SQL 入门教材会把一条查询拆成这样:
sql复制SELECT customer_id, SUM(amount)
FROM orders
WHERE status = 'paid'
GROUP BY customer_id
ORDER BY SUM(amount) DESC;
看起来语法顺序是 SELECT → FROM → WHERE → GROUP BY → ORDER BY,但数据库真正干活的时候,顺序完全不是从左到右。标准化一点的逻辑执行顺序是这样的:
| 步骤 | 执行阶段 | 作用 |
|---|---|---|
| 1 | FROM / JOIN | 确定数据来源,先把表拿过来 |
| 2 | WHERE | 按行过滤,删除不满足条件的行 |
| 3 | GROUP BY | 按指定列分组 |
| 4 | HAVING | 分组后,对组进行过滤 |
| 5 | SELECT | 计算并输出需要的列和表达式 |
| 6 | ORDER BY | 对最终结果排序 |
用一个生活类比:去食堂打饭。FROM 是走进食堂,WHERE 是把不吃的菜先排除掉,GROUP BY 是把你选的一堆菜按荤素装盘,HAVING 是发现这盘只允许装三样,不合适的再撤下去,SELECT 是最终把每盘菜端出来,ORDER BY 是你把这几个盘子按喜好排队。
这个顺序是所有查询语法配合的基础,不理解它,后面很多东西只能靠死记硬背。
1.2 执行顺序决定了三件事:别名、WHERE 限制和 HAVING 位置
先把执行顺序记住,很多你遇到的报错就能瞬间解释通了。
第一,WHERE 不能使用 SELECT 里定义的别名,因为 WHERE 执行在 SELECT 之前,别名根本还没生成。比如你想写“找出消费总额超过 10000 的客户”,下面这条会直接报错:
sql复制SELECT customer_id, SUM(amount) AS total_amount
FROM orders
WHERE total_amount > 10000
GROUP BY customer_id;
SQL Server 会直接提示“列名无效”,MySQL 某些版本也会抛异常。正确做法是把过滤放到 HAVING 里,因为 HAVING 是在 GROUP BY 之后执行的,那时候聚合结果已经算完了。
第二,WHERE 不能直接使用聚合函数,比如 SUM、COUNT、AVG。为什么?因为 WHERE 的执行时间在 GROUP BY 之前,那时候数据还没分组,聚合函数无从算起。你只能说“我要 status 等于 paid 的行”,不能说“我要 SUM(amount) 大于 10000 的行”,因为后者依赖分组结果。分组之后想过滤组,只能用 HAVING。
第三,ORDER BY 可以使用别名,因为它的执行顺序在 SELECT 之后。所以 SELECT 里写了 SUM(amount) AS total_amount,ORDER BY total_amount DESC 是可以正常运行的。很多人只记住了“WHERE 不能用别名”,却不知道为什么 ORDER BY 可以,其实就是执行顺序的差异。
这条顺序规则,建议你贴在工位上。平时写 SQL 出现莫名其妙的问题,先别急着怀疑数据库,按执行顺序走一遍,十有八九能找到原因。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. WHERE 过滤条件实战:AND/OR 优先级、NULL 和索引细节
2.1 AND 与 OR 同时出现时,别忘了括号
WHERE 是最容易“看起来对、实际错”的语法,多条件组合时问题尤其多。最常见的坑是 AND 和 OR 混用时不加括号,结果不符合预期。
举一个真实例子。需求是查询“北京地区已支付的订单,或者金额大于 1000 的所有订单”,新手经常会写成:
sql复制SELECT *
FROM orders
WHERE status = 'paid'
AND city = '北京'
OR amount > 1000;
问题在于 AND 的优先级高于 OR,上面这条的真实逻辑不是你想的那样,而是:
sql复制WHERE (status = 'paid' AND city = '北京')
OR amount > 1000;
也就是说,金额大于 1000 的订单,不管状态和地区,都会被查出来。如果你想表达的是“支付成功的订单中,北京地区或者金额大于 1000 的”,必须加括号:
sql复制SELECT *
FROM orders
WHERE status = 'paid'
AND (city = '北京' OR amount > 1000);
我见过大量线上报表数据错乱,最后排查原因就是这里少一对括号。所以在组合条件里,我的习惯是只要同时出现 AND 和 OR,一律把 OR 两侧的条件用括号包起来,不管有没有必要。这不是风格问题,是防错问题。
2.2 WHERE 里过滤空值的正确姿势
NULL 是 SQL 里最容易让人怀疑人生的概念。很多人查“金额
