MySQL分組查詢最早(大)或最晚(小)記錄
阿新 • • 發佈:2018-12-12
想必大家也遇到過查詢每日最早或最晚(最大或最小)記錄這樣的需求,那相應的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;
可以看出是使用了連線查詢,連線的部分查出了分組內的最值。