【问题标题】: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]]}。

如果有人可以帮助我,非常感谢!

【问题讨论】:

  • 这是一个常见问题解答。请在考虑发布之前阅读您的教科书和/或手册和谷歌任何错误消息或您的问题/问题/目标的许多清晰、简洁和精确的措辞,有和没有您的特定字符串/名称和站点:stackoverflow.com 和标签;阅读许多答案。如果您发布问题,请使用一个短语作为标题。反映你的研究。请参阅How to Ask 和投票箭头鼠标悬停文本。

标签: mysql join group-by subquery greatest-n-per-group


【解决方案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)

      【讨论】:

        猜你喜欢
        • 2013-12-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-02
        相关资源
        最近更新 更多