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 个或多个布尔表达式,其计算结果为 TRUEFALSE。 所以在这种情况下,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 结合函数 的统计技巧

MySQL: GROUP BY + HAVING MAX(...) --- 为啥 HAVING MAX(grade) 不会返回最高等级?

MySql - GROUP BY 和 HAVING关键字

使用 GROUP BY ... HAVING 优化 MySQL 查询时遇到问题

mysql group by having 子句