![]()
很多人刚学 SQL 的时候,最容易卡住的不是 SELECT,而是这几个关键字到底谁先写、谁先执行:
- WHERE
- GROUP BY
- HAVING
- ORDER BY
看起来只是几个子句,真写题的时候却经常一脸懵:
- 到底先过滤还是先分组?
- WHERE 和 HAVING 有什么区别?
- 为什么有时候能用别名,有时候又报错?
- 明明语句写对了,结果却不对?
这篇文章就用最常见的单表查询场景,把这几个知识点彻底讲透。看完之后,你不光能写 SQL,还能真正理解它“是怎么跑起来的”。
一、先搞清楚:SELECT 语句到底怎么写
单表查询里,最常见的完整结构如下:
SELECT 字段, 聚合函数(字段)FROM 表名WHERE 条件GROUP BY 分组字段HAVING 分组后的条件ORDER BY 排序字段 ASC|DESC;它的书写顺序是固定的:
- SELECT
- FROM
- WHERE
- GROUP BY
- HAVING
- ORDER BY
这个顺序不能乱。
比如下面这种就是错的:
SELECT job, MIN(sal)FROM empHAVING MIN(sal) < 2000GROUP BY job;因为 HAVING 不能写在 GROUP BY 前面。
二、书写顺序和执行顺序,不是一回事
这是 SQL 初学者最容易忽略的一点。
虽然你写 SQL 的时候是从 SELECT 开始写,但数据库真正执行的时候,并不是先执行 SELECT。
SQL 的常见执行顺序
FROMWHEREGROUP BYSELECTHAVINGORDER BY你可以这么理解:
1)FROM:先确定从哪张表取数据
先找到数据源。
2)WHERE:先对原始数据做第一次过滤
这是行级过滤,发生在分组之前。
3)GROUP BY:把过滤后的数据分组
按某个字段,或者多个字段进行聚合统计。
4)SELECT:选择要显示的列
这时候才真正决定最终显示哪些字段和计算结果。
5)HAVING:对分组后的结果再过滤一次
这是组级过滤。
6)ORDER BY:最后排序
排序永远是最后一步。
三、WHERE 和 HAVING,到底差在哪?
这是面试和考试里都非常高频的点。
WHERE
- 作用对象:原始记录
- 执行时机:分组前
- 适合场景:普通条件过滤
例如:
SELECT *FROM empWHERE sal > 2000;表示先把工资大于 2000 的员工筛出来。
HAVING
- 作用对象:分组后的结果
- 执行时机:分组后
- 适合场景:对聚合结果进行过滤
例如:
SELECT deptno, AVG(sal)FROM empGROUP BY deptnoHAVING AVG(sal) > 2000;表示先按部门分组,再筛选平均工资大于 2000 的部门。
一句话记忆
WHERE 过滤行,HAVING 过滤组。
这句话非常重要,很多 SQL 题本质上就在考这个。
四、单表查询实战:4个经典练习一次吃透
下面直接上实战,用 emp 表来演示。假设表结构里有这些常见字段:
- empno:员工编号
- ename:员工姓名
- job:职位
- sal:工资
- deptno:部门编号
这道题别急着写代码,先读懂意思:
- “工资最小值”说明要用聚合函数 MIN(sal)
- “职位”说明要按 job 分组
- “最小值小于 2000”说明要对分组后的结果过滤,所以要用 HAVING
SELECT job, MIN(sal) AS min_salFROM empGROUP BY jobHAVING MIN(sal) < 2000;示例运行结果JOB MIN_SALCLERK 800SALESMAN 1600ANALYST 1000结果含义:这些职位对应的最低工资低于 2000。这题为什么不能用 WHERE?
因为 MIN(sal) 是分组后才算出来的结果,WHERE 阶段根本还没分组,自然也拿不到聚合结果。
错误示例:
SELECT job, MIN(sal)FROM empWHERE MIN(sal) < 2000GROUP BY job;这类写法会直接报错。
练习2:列出平均工资大于 1200 的“部门+职位”组合题目拆解
这题的关键在于:不是按部门分组,也不是按职位分组,而是按部门和职位的组合分组。
也就是说,一个分组单位是:
- 某部门
- 某职位
所以这里要使用多字段分组。
SQL 写法
SELECT deptno, job, AVG(sal) AS avg_salFROM empGROUP BY deptno, jobHAVING AVG(sal) > 1200ORDER BY deptno ASC;示例运行结果DEPTNO JOB AVG_SAL10 MANAGER 245010 PRESIDENT 500020 ANALYST 300020 MANAGER 297530 MANAGER 285030 SALESMAN 1400这题最容易犯的错错误1:把部门编号写成员工编号很多人手一快写成 empno,这就完全变味了。
正确字段应该是:
deptno不是:
empno错误2:分组字段不完整错误示例:
SELECT deptno, job, AVG(sal)FROM empGROUP BY deptno;如果你 SELECT 里出现了 job,但 GROUP BY 里没有 job,很多数据库会直接报错。
因为数据库不知道同一个部门里多个 job 应该显示哪一个。
练习3:统计人数小于 4 的部门平均工资题目拆解
这题有两个目标:
- 统计每个部门的人数
- 找出人数小于 4 的部门
- 再展示这些部门的平均工资
所以这里要按 deptno 分组,同时用:
- COUNT(1) 统计人数
- AVG(sal) 统计平均工资
- HAVING COUNT(1) < 4 做分组过滤
SELECT deptno,COUNT(1) AS emp_count,AVG(sal) AS avg_salFROM empGROUP BY deptnoHAVING COUNT(1) < 4;示例运行结果DEPTNO EMP_COUNT AVG_SAL10 3 2916.67这题为什么很经典?因为它把两个聚合函数放在了一起:
- COUNT() 负责判断人数
- AVG() 负责输出平均工资
很多人一开始会误以为只能用一个聚合函数,其实完全可以在同一个查询里同时使用多个聚合统计。
练习4:统计各部门最高工资,并排除最高工资低于 3000 的部门
这道题的原意其实可以有两种理解,得看你想保留哪一类部门。
版本1:筛出“最高工资小于3000”的部门
如果你想找的是“哪些部门的最高工资还不到 3000”,那就这么写:
SELECT deptno, MAX(sal) AS max_salFROM empGROUP BY deptnoHAVING MAX(sal) < 3000;示例结果DEPTNO MAX_SAL30 2850这表示:30 号部门的最高工资只有 2850,说明整体薪资偏低。
版本2:排除最高工资低于3000的部门
如果题目说的是“排除最高工资小于 3000 的部门”,那实际保留的应该是最高工资大于等于 3000的部门。
写法应该是:
SELECT deptno, MAX(sal) AS max_salFROM empGROUP BY deptnoHAVING MAX(sal) >= 3000;示例结果DEPTNO MAX_SAL10 500020 3000这道题提醒我们什么?做 SQL 题时,真正难的经常不是语法,而是审题。
一定要看清楚题目到底是:
- 筛选满足条件的组
- 还是排除不满足条件的组
差一个字,SQL 逻辑就反了。
五、为什么有时候别名能用,有时候不能用?
很多人会注意到一个现象:
在 HAVING 里,有时可以写别名
SELECT deptno, AVG(sal) AS avg_salFROM empGROUP BY deptnoHAVING avg_sal > 2000;某些数据库支持这种写法,因为 HAVING 执行时机晚于 SELECT。
但为了兼容性更好,建议你直接写完整表达式:
HAVING AVG(sal) > 2000这样更稳。
在 WHERE 里通常不能直接用别名
因为 WHERE 执行在前,SELECT 里的别名此时还没生成。
比如:
SELECT sal * 12 AS annual_salFROM empWHERE annual_sal > 20000;这类写法通常不成立。
六、写聚合查询时,最实用的审题方法
以后碰到 SQL 聚合题,直接按这三步走,基本不容易错。
第一步:先确定按什么分组
题目里如果出现:
- 每个部门
- 每种职位
- 每个部门和职位组合
这些词,通常就是在提示你 GROUP BY 应该写什么。
第二步:再确定统计什么
题目里如果出现:
- 平均工资 → AVG(sal)
- 最低工资 → MIN(sal)
- 最高工资 → MAX(sal)
- 人数 → COUNT(1) 或 COUNT(*)
这一步决定聚合函数。
第三步:最后判断条件写 WHERE 还是 HAVING
这是最关键的判断标准:
- 如果条件是针对原始字段,比如 sal > 1000,用 WHERE
- 如果条件是针对统计结果,比如 AVG(sal) > 2000,用 HAVING
假设现在有个需求:
查询 20 号和 30 号部门中,平均工资大于 1500 的职位,并按平均工资降序排列。SQL 写法
SELECT deptno, job, AVG(sal) AS avg_salFROM empWHERE deptno IN (20, 30)GROUP BY deptno, jobHAVING AVG(sal) > 1500ORDER BY avg_sal DESC;执行逻辑拆解第1步:FROM emp从 emp 表取数据。
第2步:WHERE deptno IN (20, 30)
先把 20 号和 30 号部门之外的数据过滤掉。
第3步:GROUP BY deptno, job
按“部门+职位”组合分组。
第4步:SELECT deptno, job, AVG(sal)
计算每组平均工资,并选择要显示的列。
第5步:HAVING AVG(sal) > 1500
只保留平均工资大于 1500 的组。
第6步:ORDER BY avg_sal DESC
按平均工资从高到低排序。
示例运行结果
DEPTNO JOB AVG_SAL20 ANALYST 300020 MANAGER 297530 MANAGER 2850八、几个非常容易踩坑的地方,提前避开1. WHERE 和 HAVING 混用这是最常见的错。
错误思路:
- 还没分组就想过滤平均工资
- 已经是原始字段条件却硬写到 HAVING
正确顺序永远是:
SELECTFROMWHEREGROUP BYHAVINGORDER BY别自己“自由发挥”。
3. GROUP BY 后,SELECT 中非聚合字段要保持一致
如果 SELECT 里出现普通字段,它通常就必须出现在 GROUP BY 中。
4. 审题不认真,逻辑写反
比如:
- “小于 3000 的部门”
- “排除小于 3000 的部门”
这两个 SQL 完全不是一回事。
九、建议收藏的几个高频模板1)按单字段分组统计
SELECT job, AVG(sal)FROM empGROUP BY job;2)按多字段分组统计SELECT deptno, job, AVG(sal)FROM empGROUP BY deptno, job;3)分组后过滤SELECT deptno, COUNT(*)FROM empGROUP BY deptnoHAVING COUNT(*) < 4;4)先过滤再分组SELECT deptno, AVG(sal)FROM empWHERE sal > 1000GROUP BY deptno;5)分组后排序SELECT deptno, AVG(sal) AS avg_salFROM empGROUP BY deptnoORDER BY avg_sal DESC;十、总结:真正把 SQL 聚合查询想明白单表查询看起来只是几行 SQL,实际上考的是三件事:
1. 你有没有看懂题
先分组什么,再统计什么,最后过滤什么。
2. 你知不知道 WHERE 和 HAVING 的边界
一个管原始数据,一个管分组结果。
3. 你是否理解执行顺序
写在前面的,不一定先执行;写在后面的,也不一定最后参与逻辑判断。
如果把这三件事想透了,后面的多表查询、子查询、复杂统计都会顺很多。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.