MySQL分组查询GROUP BY
GROUP BY子句
select中使用GROUP BY子句可以对指定列进行分组查询。
**需要满足:**使用GROUP BY进行分组查询时,SELECT指定的字段必须是“分组依据字段”(要对哪个列进行分组),其他没有指定分组字段若想要出现在SELECT中则必须包含在聚合函数中。
示例:
-- 创建示例用表
drop table if exists emp;
create table emp (id bigint primary key auto_increment,name varchar(20) not null comment '员工姓名';role varchar(20) not null comment '职位';salery decimal(10,2) not null comment '工资';
)-- 计算不通角色工资的平均值
select role,avg(salary) from emp group by role;-- 使用ROUND(数值,小数点位数)
select role,round(avg(salary),2) as 平均工资 from emp group by role;-- group by 子句后面可以跟order by子句
select role,round(avg(salary),2) as 平均工资 from emp group by role order by 平均工资 ASC;
ROUND()
是一个常用的数值函数,核心作用是将指定数值按需求四舍五入到指定的小数位数,也可用于对整数位进行四舍五入(如四舍五入到十位、百位)。
语法
ROUND(数值表达式, 小数位数)
-
数值表达式:必填,需要进行四舍五入的数值(可以是直接的数字、字段名、或其他数值计算结果,如
score * 0.8
)。 -
小数位数
:可选,整数类型,用于指定四舍五入后的小数保留位数:
- 当值为 正数 时:保留指定的小数位(如
2
表示保留 2 位小数)。 - 当值为 负数 时:对整数部分进行四舍五入(如
-1
表示四舍五入到十位,-2
表示四舍五入到百位)。 - 当值为 0 或省略 时:默认四舍五入到整数(保留 0 位小数)。
- 当值为 正数 时:保留指定的小数位(如
HAVING
GROUP BY子句分组后,需要对分组结果再进行条件过滤时,不能使用WHERE语句,而需要使用HAVING
where是对表中每一行的真实数据进行过滤的,having是对ground by之后,通过聚合函数计算出来的结果进行过滤的
示例:
select role,avg(salary) from emp group by role having avg(salary) > 10000 and avg(salary) < 20000
where用在from表名之后,也就是分组之前,而HAVING跟在group by 子句之后
-- 查询每个职位的最高工资、最低工资、平均工资
select role,max(salary),min(salary),avg(salary) from emp group by role;-- 显示平均工资低于1500的职位并他的平均工资
-- 1.按职位分组 2.使用相应的聚合函数 3.使用having子句对分组的结果进行过滤
select role,avg(salary) from emp group by role having avg(salary)<1500;
在SQL中关键的语句的执行顺序:
FROM/WHERE→
GROUP BY→
聚合计算(如 AVG(salary))→
HAVING→
SELECT(含别名定义)
别名的定义在having和where之后,所以在这两个关键字的子句中不能使用别名。
标准SQL中不允许在GROUP BY中直接使用SELECT别名,但是在MySQL允许在GROUP BY中使用SELECT别名,这是MySQL的扩展功能。
当使用多个字段进行 GROUP BY
比如:
GROUP BY student.id, student.name;
结果集会按照指定的多个字段的组合进行分组,也就是说:
- 数据库会先按照第一个字段(
student.id
)分组 - 如果第一个字段的值相同,再按照第二个字段(
student.name
)进一步分组 - 最终,只有当这两个字段的值完全相同时,才会被分到同一个组里
举例说明
假设有表 student
:
id | name | score |
---|---|---|
1 | 张三 | 80 |
1 | 张三 | 85 |
1 | 李四 | 90 |
2 | 王五 | 75 |
2 | 王五 | 78 |
执行:
SELECT id, name, COUNT(*) AS cnt
FROM student
GROUP BY id, name;
结果:
id | name | cnt |
---|---|---|
1 | 张三 | 2 |
1 | 李四 | 1 |
2 | 王五 | 2 |
解释:
id=1, name=张三
出现了 2 次 → 分到同一组id=1, name=李四
只出现了 1 次 → 分到另一组id=2, name=王五
出现了 2 次 → 分到同一组
特点
- 分组顺序重要:先按第一个字段分,再按第二个字段细分
- 组的唯一性:多个字段组合值唯一才能成为一个组
- 结果集行数:等于不同组合值的数量
SQL 标准中的规定
SQL 标准规定:
当使用
GROUP BY
时,SELECT 列表中的列必须满足以下条件之一:
- 出现在
GROUP BY
子句中- 被包含在聚合函数中(如
SUM
、COUNT
、MAX
等)
原因:
GROUP BY
会将多行数据聚合成一行(每个分组一行)。如果某个列既没有分组,也没有聚合,那么它在分组内可能有多个不同的值,数据库不知道该返回哪一个,因此会报错。
为什么有些情况下可以省略
虽然 SQL 标准要求严格,但某些数据库(例如 MySQL 默认配置下)允许非聚合、非分组的列出现在 SELECT
列表中,这被称为 ONLY_FULL_GROUP_BY 模式关闭。
当你在 MySQL 中关闭 ONLY_FULL_GROUP_BY
时:
- 如果分组内的该列值全部相同,MySQL 会随便返回该分组的一个值(实际上是第一个遇到的值)。
- 如果分组内的值不相同,MySQL 会任意取一个值返回,这可能导致不可预期的结果。
例如:
SELECT student.id, student.name
FROM student
GROUP BY student.id;
这里 student.name
没在 GROUP BY
里,也没有聚合,但:
- 如果一个
id
只对应一个name
(主键与唯一约束),那么每个分组内的name
一定相同,返回的值是确定的。 - 如果一个
id
对应多个不同的name
(数据异常),那么返回的name
就是随机的。
满足SQL标准的写法
为了代码的可移植性和结果的确定性,建议:
- 如果要显示该列,就把它加入
GROUP BY
例如:
-- 方法:加入 GROUP BY
SELECT student.id, student.name
FROM student
GROUP BY student.id, student.name;