MySQL: GROUP BY + HAVING MAX(...) --- 为什么 HAVING MAX(grade) 不会 return maximum grade?

MySQL: GROUP BY + HAVING MAX(...) --- Why HAVING MAX(grade) will not return maximum grade?

学分 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

我认为 return 应该是 {"headers": ["student_id", "course_id", "grade"], "values": [[1, 2, 99], [2, 2, 95], [3, 3, 82]]};然而,实际的 return 是 {"headers": ["student_id", "course_id", "grade"], "values": [[1, 1, 90], [2, 2, 95], [3, 1, 80]]}.

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

看起来您需要在 FROM 子句中使用子查询来处理双 GROUP BY。

在下面的查询中,子查询获取每个用户的最大成绩,然后外部注册 table 在 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;

也许你认为这个条件:

having max(grade)

是一条指令,因此只应返回每个 studentid 具有最高成绩的行。
这不是 HAVING 子句的作用。
它是一种在聚合完成后过滤聚合数据的方法,当它在 GROUP BY 子句之后使用时。
HAVING 子句接受 1 个或多个计算结果为 TRUEFALSE 的布尔表达式。
所以在这种情况下 max(grade) 不是布尔表达式(尽管对于 MySql 任何数字表达式都可以用来代替布尔表达式)。

我知道您希望在结果中显示每个 studentid.
最高成绩的行 这可以使用 MySQL 8.0 中的 window 函数以最有效和最高效的方式完成: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    |

这是一个典型的top-1-per-group问题。解决这个问题的关键在于,既然要整条记录,就不要想聚合,而是过滤.

我会为此推荐一个相关的子查询。这是一个可移植的解决方案,适用于大多数数据库(包括不支持 window 函数的 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).