MySQL: GROUP BY + HAVING MAX(...) --- 为啥 HAVING MAX(grade) 不会返回最高等级?
Posted
技术标签:
【中文标题】MySQL: GROUP BY + HAVING MAX(...) --- 为啥 HAVING MAX(grade) 不会返回最高等级?【英文标题】:MySQL: GROUP BY + HAVING MAX(...) --- Why HAVING MAX(grade) will not return maximum grade?MySQL: GROUP BY + HAVING MAX(...) --- 为什么 HAVING MAX(grade) 不会返回最高等级? 【发布时间】:2020-11-06 15:42:04 【问题描述】:学分 Leetcode 1112。每个学生的最高分
要求:编写一个 SQL 查询来查找每个学生对应课程的最高成绩。如果出现平局,您应该找到 course_id 最小的课程。输出必须按 student_id 递增排序。
查询结果格式如下例:
Enrollments table:
+------------+-------------------+
| student_id | course_id | grade |
+------------+-----------+-------+
| 2 | 2 | 95 |
| 2 | 3 | 95 |
| 1 | 1 | 90 |
| 1 | 2 | 99 |
| 3 | 1 | 80 |
| 3 | 2 | 75 |
| 3 | 3 | 82 |
+------------+-----------+-------+
Result table:
+------------+-------------------+
| student_id | course_id | grade |
+------------+-----------+-------+
| 1 | 2 | 99 |
| 2 | 2 | 95 |
| 3 | 3 | 82 |
为什么这行不通?
select student_id, course_id, grade
from enrollments
group by student_id
having max(grade)
order by student_id
我认为返回应该是 "headers": ["student_id", "course_id", "grade"], "values": [[1, 2, 99], [2, 2, 95], [ 3, 3, 82]];但是,实际返回的是 "headers": ["student_id", "course_id", "grade"], "values": [[1, 1, 90], [2, 2, 95], [3, 1 , 80]]。
如果有人可以帮助我,非常感谢!
【问题讨论】:
这是一个常见问题解答。请在考虑发布之前阅读您的教科书和/或手册和谷歌任何错误消息或您的问题/问题/目标的许多清晰、简洁和精确的措辞,有和没有您的特定字符串/名称和站点:***.com 和标签;阅读许多答案。如果您发布问题,请使用一个短语作为标题。反映你的研究。请参阅How to Ask 和投票箭头鼠标悬停文本。 【参考方案1】:也许你认为这个条件:
having max(grade)
是一条指令,因此只应返回每个 studentid
具有最高等级的行。
这不是 HAVING
子句的作用。
在GROUP BY
子句之后使用聚合完成后,它是一种过滤聚合数据的方法。HAVING
子句接受 1 个或多个布尔表达式,其计算结果为 TRUE
或 FALSE
。
所以在这种情况下,max(grade)
不是布尔表达式(尽管对于 mysql,任何数字表达式都可以用来代替布尔表达式)。
我了解您希望结果中包含每个 studentid
的最高等级的行。
这可以使用 MySQL 8.0 中的窗口函数以最有效和最高效的方式完成:ROW_NUMBER()
或 RANK()
如果您还希望返回关系:
select e.student_id, e.course_id, e.grade
from (
select *, row_number() over (partition by student_id order by grade desc) rn
from Enrollments
) e
where e.rn = 1
请参阅demo。 结果:
| student_id | course_id | grade |
| ---------- | --------- | ----- |
| 1 | 2 | 99 |
| 2 | 2 | 95 |
| 3 | 3 | 82 |
【讨论】:
【参考方案2】:看起来您需要在 FROM 子句中使用子查询来处理双重 GROUP BY。
在下面的查询中,子查询获取每个用户的最高成绩,然后外部注册表连接 student_id 和成绩。然后它在外部查询中获取第一个 course_id。
SELECT e.student_id, min(e.course_id) course_id, e.grade
FROM enrollments e
JOIN (
SELECT student_id, max(grade) grade
FROM enrollments
GROUP BY student_id) g USING (student_id, grade)
GROUP BY e.studentId;
【讨论】:
【参考方案3】:这是一个典型的 top-1-per-group 问题。解决这个问题的关键是,既然你想要整个记录,你不应该考虑聚合,而应该考虑过滤。
我会为此推荐一个相关的子查询。这是一个可移植的解决方案,适用于大多数数据库(包括不支持窗口函数的 MySQL 5.x 版本)。有了正确的索引,这通常是一种非常有效的方法。
select e.*
from enrollments e
where e.grade = (
select max(e1.grade)
from enrollments e1
where e1.student_id = e.student_id
)
这里你想要的索引是(student_id, grade)
。
【讨论】:
以上是关于MySQL: GROUP BY + HAVING MAX(...) --- 为啥 HAVING MAX(grade) 不会返回最高等级?的主要内容,如果未能解决你的问题,请参考以下文章
[MySQL]group by 与 having 结合函数 的统计技巧
MySQL: GROUP BY + HAVING MAX(...) --- 为啥 HAVING MAX(grade) 不会返回最高等级?