MySQL中HAVING子句如何筛选行?分组查各班最高分结果异常
关于MySQL中HAVING子句筛选逻辑及查询异常的问题
场景与问题描述
现有存储学生成绩的sc表,表创建语句如下:
create table sc ( `classid` int, `studentid` int, `score` int );
样本数据:
+---------+-----------+-------+ | classid | studentid | score | +---------+-----------+-------+ | 1 | 1 | 50 | | 1 | 2 | 59 | | 1 | 3 | 80 | | 1 | 4 | 68 | | 1 | 5 | 70 | | 1 | 6 | 20 | | 1 | 7 | 90 | | 1 | 8 | 100 | | 1 | 9 | 25 | | 2 | 1 | 51 | | 2 | 2 | 59 | | 2 | 3 | 80 | | 2 | 4 | 68 | | 2 | 5 | 70 | | 2 | 6 | 30 | | 2 | 7 | 44 | | 2 | 8 | 80 | | 3 | 1 | 20 | | 1 | 11 | 30 | | 1 | 12 | 40 | +---------+-----------+-------+
想要查询每个班级的最高分,编写了如下SQL语句:
select * from sc group by classid having score = max(score);
但输出结果不符合预期,仅返回一行数据:
+---------+-----------+-------+ | classid | studentid | score | +---------+-----------+-------+ | 3 | 1 | 20 | +---------+-----------+-------+
请问MySQL中HAVING子句是如何筛选行的,为何查询结果异常?
解答
1. MySQL中HAVING子句的筛选逻辑
HAVING子句的作用是对GROUP BY分组后的结果集进行筛选,它仅能使用分组字段、聚合函数,或者被GROUP BY包含的字段(MySQL非严格模式下允许使用非分组字段,但这不符合SQL标准)。
注意:HAVING在分组完成后执行,而WHERE是在分组前过滤行。
2. 原SQL结果异常的原因
你的SQL存在两个核心问题:
- 非分组字段直接出现在SELECT和HAVING中:用
GROUP BY classid分组后,studentid和score都不属于分组字段。MySQL默认非严格模式下不会报错,但会返回分组中随机一行数据的studentid和score值,而非该组所有行。 - HAVING条件逻辑错误:
score = max(score)中,左边的score是分组后随机取出的某一行分数,右边的max(score)是该组最高分。只有当随机取出的行刚好是该组最高分的时候,才会被筛选出来。班级3只有一行数据,所以随机取的就是最高分,因此被返回;而班级1、2随机取的行不是最高分,所以被过滤掉了。
3. 正确的查询写法
要获取每个班级的最高分及对应学生信息,有几种常见写法:
写法一:子查询关联原表
SELECT sc.* FROM sc JOIN ( SELECT classid, MAX(score) AS max_score FROM sc GROUP BY classid ) AS class_max ON sc.classid = class_max.classid AND sc.score = class_max.max_score;
写法二:窗口函数(MySQL 8.0+支持)
SELECT classid, studentid, score FROM ( SELECT *, RANK() OVER (PARTITION BY classid ORDER BY score DESC) AS rnk FROM sc ) AS ranked WHERE rnk = 1;
这种写法可处理同班级多个最高分的情况(比如班级2有两个80分,都会被返回)。
内容的提问来源于stack exchange,提问作者JASON CHAN
相关产品推荐
相关产品推荐

