[简记]MySQL下GroupBy与Max结合获取分组最大的正确用法

272

环境

mysql version : 8
示例表: score

create table score (
    id int not null primary key  auto_increment comment '主键',
    name varchar(32) not null comment '名字',
    score_date date not null comment '考试时间',
    score int not null comment '分数'
) comment '分数表';

数据准备

  1. 执行数据新增语句
INSERT INTO test.score (id, name, score_time, score) VALUES (1, 'Alice', '2021-01-25', 90);
INSERT INTO test.score (id, name, score_time, score) VALUES (2, 'Alice', '2021-01-24', 90);
INSERT INTO test.score (id, name, score_time, score) VALUES (3, 'Alice', '2021-01-22', 50);
INSERT INTO test.score (id, name, score_time, score) VALUES (4, 'Alice', '2021-01-21', 30);
INSERT INTO test.score (id, name, score_time, score) VALUES (5, 'Bob', '2021-01-21', 90);
INSERT INTO test.score (id, name, score_time, score) VALUES (6, 'Alice', '2021-01-25', 50);
INSERT INTO test.score (id, name, score_time, score) VALUES (7, 'Bob', '2021-01-22', 90);
INSERT INTO test.score (id, name, score_time, score) VALUES (8, 'Bob', '2021-01-25', 30);
INSERT INTO test.score (id, name, score_time, score) VALUES (9, 'Bob', '2021-01-24', 90);
  1. 得到Alice和Bob每天考试分数如下
+----+-------+------------+-------+
| id | name  | score_time | score |
+----+-------+------------+-------+
|  1 | Alice | 2021-01-25 |    90 |
|  2 | Alice | 2021-01-24 |    90 |
|  3 | Alice | 2021-01-22 |    50 |
|  4 | Alice | 2021-01-21 |    30 |
|  5 | Bob   | 2021-01-21 |    90 |
|  6 | Alice | 2021-01-25 |    50 |
|  7 | Bob   | 2021-01-22 |    90 |
|  8 | Bob   | 2021-01-25 |    30 |
|  9 | Bob   | 2021-01-24 |    90 |
+----+-------+------------+-------+

需求描述

使用SQL查询出表T中以A列为分组的B列最大值情况下的C列最大下的所有行。
如使用sql查询出Alice和Bob分数最高的最新的一天的记录。对于这条描述应当是:在 _分数表(T:score)_ 中以 名字(A:name) 分组, 查询出 分数(B:score) 最高的 时间最近(C:score_time) 的记录,结果应当是:

+----+-------+------------+-------+
| id | name  | score_time | score |
+----+-------+------------+-------+
|  1 | Alice | 2021-01-25 |    90 |
|  5 | Bob   | 2021-01-24 |    90 |
+----+-------+------------+-------+

一般我们查询的需求是一次group by 与 一次 max,比如查询出Alice和Bob最大的分数时间记录,但这次有点不一样的不就是时间记录还要是最近的时间记录,就是说时间也要是符合要求中的最大的。

验证

用法1(错误):

select id, name, max(score), max(score_time) from score group by name

这个用法sql是错误的,无法执行。这个错误发生在mysql 5.7 版本及以上版本会出现的问题,提示以下错误

sql_mode=only_full_group_by

两种处理方式,一个是修改sql_model,第二个是修改被选择的列

select name, max(score), max(score_time) from score group by name

结果如下:

+----+-------+------------+-------+
| id | name  | score_time | score |
+----+-------+------------+-------+
|  1 | Alice | 2021-01-25 |    90 |
|  5 | Bob   | 2021-01-21 |    90 |
+----+-------+------------+-------+

用法2(正确):

两层group by与max的嵌套,也就是第一个group by和max找出符合第一个条件的数据,第二个group by与max找出基于第一条件的情况的符合第二条件的数据

select score.name, max(score.score_time), score.score from score left join (
	select name, max(score) as score from score group by name
) as high_score on high_score.name = score.name and high_score.score = score.score where high_score.name is not null group by score.name, score.score;

结果,正确:

+-------+-----------------------+-------+
| name  | max(score.score_time) | score |
+-------+-----------------------+-------+
| Alice | 2021-01-25            |    90 |
| Bob   | 2021-01-24            |    90 |
+-------+-----------------------+-------+

效率:

+-------+-----------------------+---------+
| 数据量 | 索引                 | 时间/ms |
+-------+-----------------------+---------+
| 1w    | 无                    |    90   |
| 1w    | 组合索引:name,score   |    20   |
+-------+-----------------------+---------+

参考

  1. MySQL MAX() function with group by