考点频率 :★★★★★(下午题必考,选择题常考,尤其是 WHERE 与 HAVING 的区别)
难度 :⭐⭐⭐⭐
建议:重点掌握 GROUP BY 的用法、HAVING 与 WHERE 的本质区别、聚合函数的正确使用位置
1️⃣ 从"查一条"到"算一组"
上一篇文章我们学了 SELECT 的基本用法------查具体的数据行(比如"查所有年龄大于20岁的学生")。
但在实际业务中,我们经常需要的不只是"列出数据",而是对数据进行汇总统计:
- "每个班有多少人?"
- "各科的平均分是多少?"
- "哪个学生的总分最高?"
这些查询不再返回"原始行",而是返回统计结果 。这就需要用到 分组查询(GROUP BY) 和 聚合函数。
打个比方 :基本查询就像翻花名册 ,一页一页看。高级查询就像做统计报表------不用看每个人的具体信息,而是问"一共有多少人?""男女比例多少?"。
2️⃣ 聚合函数(复习+补充)
聚合函数对一组值进行计算,返回单个值。上一篇文章已经介绍过,这里快速回顾:
| 函数 | 作用 | 说明 |
|---|---|---|
COUNT(*) |
统计行数 | 包含 NULL 行 |
COUNT(列名) |
统计非空值个数 | 不包含 NULL |
SUM(列名) |
求和 | 只适用于数值列 |
AVG(列名) |
求平均值 | 只适用于数值列 |
MAX(列名) |
求最大值 | 适用于任意可比较类型 |
MIN(列名) |
求最小值 | 适用于任意可比较类型 |
重点 :聚合函数会忽略 NULL 值 (
COUNT(*)除外)。比如AVG(成绩)只计算有成绩的记录,NULL 不参与计算。
3️⃣ GROUP BY------分组聚合
3.1 基本用法
GROUP BY 将表中的数据按某个(或某几个)列进行分组,然后对每个组分别应用聚合函数。
语法:
sql
SELECT 分组列, 聚合函数(列)
FROM 表名
GROUP BY 分组列;
示例:统计每个班级的人数
sql
SELECT 班级号, COUNT(*) AS 人数
FROM 学生表
GROUP BY 班级号;
| 班级号 | 人数 |
|---|---|
| 101 | 35 |
| 102 | 32 |
| 103 | 28 |
3.2 GROUP BY 的核心规则(软考常考)
规则1 :SELECT 子句中出现的非聚合列 ,必须出现在 GROUP BY 中。
sql
-- ✅ 正确:班级号在 GROUP BY 中
SELECT 班级号, AVG(年龄) FROM 学生表 GROUP BY 班级号;
-- ❌ 错误:姓名不在 GROUP BY 中
SELECT 姓名, AVG(年龄) FROM 学生表 GROUP BY 班级号;
原因是:分组后每个组可能有多行,
姓名这个列没有聚合,数据库不知道应该取组里的哪一个姓名,所以报错。
规则2 :GROUP BY 中可以包含多个列(先按第一列分组,再按第二列分组)。
sql
-- 先按班级号分组,再按性别分组
SELECT 班级号, 性别, COUNT(*) AS 人数
FROM 学生表
GROUP BY 班级号, 性别;
3.3 分组前筛选:WHERE(先筛选,再分组)
WHERE 在 GROUP BY 之前 执行,用于先过滤掉不需要的行,再对剩余行进行分组。
sql
-- 查询年龄大于18岁的学生中,每个班级的人数
SELECT 班级号, COUNT(*) AS 人数
FROM 学生表
WHERE 年龄 > 18
GROUP BY 班级号;
执行顺序 :先 WHERE 筛选年龄>18 → 再 GROUP BY 按班级分组 → 最后 COUNT(*) 统计。
3.4 分组后筛选:HAVING(先分组,再筛选)
HAVING 在 GROUP BY 之后 执行,用于筛选分组后的结果。
sql
-- 查询人数大于30的班级
SELECT 班级号, COUNT(*) AS 人数
FROM 学生表
GROUP BY 班级号
HAVING COUNT(*) > 30;
执行顺序 :先 GROUP BY 分组 → 再 COUNT(*) 统计 → 最后 HAVING 筛选人数>30。
3.5 WHERE vs HAVING(核心区别,必考!)
这是软考选择题的高频陷阱题,务必分清:
| 对比项 | WHERE | HAVING |
|---|---|---|
| 执行时机 | 分组前(先筛选再分组) | 分组后(先分组再筛选) |
| 能否使用聚合函数 | ❌ 不能 | ✅ 可以 |
| 作用的对象 | 单个元组(行) | 分组后的组 |
| 能否使用列别名 | ❌ 不能 | ✅ 可以(部分DBMS) |
一句话记忆 :WHERE 筛行(分组前),HAVING 筛组(分组后)。
sql
-- ❌ 错误:WHERE 中不能使用聚合函数
SELECT 班级号, COUNT(*) FROM 学生表 WHERE COUNT(*) > 30 GROUP BY 班级号;
-- ✅ 正确:HAVING 可以使用聚合函数
SELECT 班级号, COUNT(*) FROM 学生表 GROUP BY 班级号 HAVING COUNT(*) > 30;
4️⃣ 分组+排序+筛选的综合示例
完整查询结构:
sql
SELECT 列名, 聚合函数
FROM 表名
WHERE 行条件 -- 1. 筛选行(分组前)
GROUP BY 列名 -- 2. 分组
HAVING 组条件 -- 3. 筛选组(分组后)
ORDER BY 列名; -- 4. 排序
示例:查询平均年龄大于19岁的班级,按平均年龄降序排列。
sql
SELECT 班级号, AVG(年龄) AS 平均年龄
FROM 学生表
WHERE 性别 = '男' -- 只统计男生
GROUP BY 班级号
HAVING AVG(年龄) > 19 -- 平均年龄大于19
ORDER BY 平均年龄 DESC; -- 从大到小排列
5️⃣ 经典例题
例题1:查询"选课"表中,各门课程的平均成绩,并只显示平均成绩大于80分的课程。
解析:
sql
SELECT 课程号, AVG(成绩) AS 平均成绩
FROM 选课表
GROUP BY 课程号
HAVING AVG(成绩) > 80;
先按课程号分组,再计算每组的平均成绩,最后筛选平均成绩>80的课程。
例题2:以下 SQL 语句中,正确的是( )。
A. SELECT 班级号, COUNT(*) FROM 学生表 WHERE COUNT(*) > 10 GROUP BY 班级号;
B. SELECT 班级号, AVG(年龄) FROM 学生表 GROUP BY 班级号 HAVING AVG(年龄) > 20;
C. SELECT 班级号, 姓名, COUNT(*) FROM 学生表 GROUP BY 班级号;
D. SELECT 班级号, COUNT(*) FROM 学生表 GROUP BY 班级号 WHERE COUNT(*) > 10;
解析:
- A 错误:WHERE 中不能使用聚合函数。
- B 正确:HAVING 可以使用聚合函数。
- C 错误:
姓名不在 GROUP BY 中,却出现在 SELECT 中(非聚合列)。 - D 错误:WHERE 必须在 GROUP BY 之前。
选 B。
例题3 (判断):HAVING 子句和 WHERE 子句的作用完全相同,可以互换使用。( )
解析 :错误。WHERE 在分组前筛选行,HAVING 在分组后筛选组;WHERE 不能使用聚合函数,HAVING 可以使用。
6️⃣ 执行顺序总结
sql
SELECT 班级号, AVG(年龄) AS 平均年龄 -- 第5步:选择列
FROM 学生表 -- 第1步:确定数据来源
WHERE 性别 = '男' -- 第2步:筛选行(不能用聚合函数)
GROUP BY 班级号 -- 第3步:分组
HAVING AVG(年龄) > 19 -- 第4步:筛选组(可以用聚合函数)
ORDER BY 平均年龄 DESC; -- 第6步:排序
7️⃣ 记忆口诀
WHERE 先筛行,GROUP BY 再分组。
聚合函数算统计,HAVING 后筛组。
WHERE 不能有聚合,HAVING 专治聚合条件。
8️⃣ 小测验(评论区对答案)
SQL 语句:
SELECT 班级号, COUNT(*) AS 人数 FROM 学生表 GROUP BY 班级号 HAVING 人数 > 30;请问:这条语句中
人数这个别名可以在HAVING中使用吗?(不同 DBMS 支持情况不同,软考中通常按 SQL 标准判定)A. 可以,HAVING 中可以使用 SELECT 中定义的别名
B. 不可以,HAVING 中不能使用别名
C. 可以,只要该别名在 SELECT 中定义
D. 取决于 WHERE 子句
🔔 本专栏日更,点击头像 → 专栏《软考中级高频考点》订阅,第一时间接收新内容
#软考中级 #软件设计师 #SQL #GROUP BY #HAVING #聚合函数 #数据库技术基础 #软考备考