1. 程式人生 > >MySQL分組查詢最早(大)或最晚(小)記錄

MySQL分組查詢最早(大)或最晚(小)記錄

想必大家也遇到過查詢每日最早或最晚(最大或最小)記錄這樣的需求,那相應的SQL應該怎麼寫呢?

下面以查詢玩家每日最早或最晚登陸記錄作為例子記錄下。先弄下測試表和資料:

CREATE TABLE `test`.`player_login` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `uid` VARCHAR(45) NOT NULL,
  `time` INT NOT NULL,
  PRIMARY KEY (`id`),
  INDEX `player_login_uid` (`uid` ASC),
  INDEX `player_login_time` (`time` ASC));

insert into test.player_login(uid,`time`) values('1',unix_timestamp('2018-09-10 08:10:30'));
insert into test.player_login(uid,`time`) values('2',unix_timestamp('2018-09-10 08:11:10'));
insert into test.player_login(uid,`time`) values('3',unix_timestamp('2018-09-10 08:15:01'));
insert into test.player_login(uid,`time`) values('4',unix_timestamp('2018-09-10 08:20:05'));

insert into test.player_login(uid,`time`) values('1',unix_timestamp('2018-09-10 10:10:30'));
insert into test.player_login(uid,`time`) values('2',unix_timestamp('2018-09-10 10:20:15'));
insert into test.player_login(uid,`time`) values('3',unix_timestamp('2018-09-10 11:05:30'));
insert into test.player_login(uid,`time`) values('4',unix_timestamp('2018-09-10 10:30:45'));

insert into test.player_login(uid,`time`) values('2',unix_timestamp('2018-09-11 07:20:15'));
insert into test.player_login(uid,`time`) values('3',unix_timestamp('2018-09-11 09:05:30'));
insert into test.player_login(uid,`time`) values('4',unix_timestamp('2018-09-11 10:30:45'));

insert into test.player_login(uid,`time`) values('3',unix_timestamp('2018-09-11 20:05:30'));
insert into test.player_login(uid,`time`) values('4',unix_timestamp('2018-09-11 21:30:45'));

insert into test.player_login(uid,`time`) values('4',unix_timestamp('2018-09-12 20:30:45'));
insert into test.player_login(uid,`time`) values('4',unix_timestamp('2018-09-12 23:30:45'));

對於查詢最早記錄最初寫了個這樣的SQL:

select uid, `time`, date_format(from_unixtime(`time`), '%Y-%m-%d') d, from_unixtime(`time`) loginTime from test.player_login group by uid, d order by d desc,  uid desc;

還湊效,也許是因為記錄都是按時間順序插入的,符合MySQL group by時對分組內的記錄按某種順序排序。但如果查詢每日最晚登陸呢?傻逼了。於是想到了子查詢:

select * 
from (
	select uid, `time`, date_format(from_unixtime(`time`), '%Y-%m-%d') d, from_unixtime(`time`) loginTime
    from test.player_login 
    order by `time` desc
) a
group by uid, a.d order by d desc, uid desc;

滿心歡喜,走一個。WTF,和查詢最早的結果一樣,逗我呢!按道理講,應該能得到正確的結果的啊!通過查詢,知道真相的我眼淚掉下來。如果你是在MySQL5.7之前版本中測試,恭喜你得到了想要的結果,如果是之後(包含)的版本,同樣恭喜你,得到了和我一樣的結果。病危通知書:5.7版本以後對子查詢排序做了優化,子查詢全表排序失效。

不皮了,直接說結果吧:

select a.uid, a.`time`, date_format(from_unixtime(a.`time`), '%Y-%m-%d')  as d, from_unixtime(a.`time`) as loginTime 
from test.player_login a 
join (
	select uid, max(`time`) `latestTime` 
    from test.player_login 
    group by uid, date_format(from_unixtime(`time`), '%Y-%m-%d')) b 
on a.uid=b.uid and a.`time`=b.`latestTime` 
order by a.`time` desc, a.uid desc;

select a.uid, a.`time`, date_format(from_unixtime(a.`time`), '%Y-%m-%d')  as d, from_unixtime(a.`time`) as loginTime 
from test.player_login a 
join (
	select uid, min(`time`) `earlestTime` 
    from test.player_login 
    group by uid, date_format(from_unixtime(`time`), '%Y-%m-%d')) b 
on a.uid=b.uid and a.`time`=b.`earlestTime` 
order by a.`time` desc, a.uid desc;

可以看出是使用了連線查詢,連線的部分查出了分組內的最值。