1. 程式人生 > 其它 >sql獲取group by最後一條記錄

sql獲取group by最後一條記錄

獲取group by最後一條記錄

建立測試表

create table login_log (id int primary key auto_increment, uid int, login_at datetime, login_device varchar(16))

插入資料

INSERT INTO `login_log` (`id`, `uid`, `login_at`, `login_device`) VALUES (1, 1, '2022-04-01 14:34:54', 'PC');
INSERT INTO `login_log` (`id`, `uid`, `login_at`, `login_device`) VALUES (2, 2, '2022-03-01 14:35:08', 'MOBILE');
INSERT INTO `login_log` (`id`, `uid`, `login_at`, `login_device`) VALUES (3, 1, '2022-04-14 14:35:27', 'WEB');
INSERT INTO `login_log` (`id`, `uid`, `login_at`, `login_device`) VALUES (4, 2, '2022-04-30 14:35:35', 'WEB');

檢視資料

select * from login_log;
id	uid login_at           	login_device
1	1	2022-04-01 14:34:54	PC
2	2	2022-03-01 14:35:08	MOBILE
3	1	2022-04-14 14:35:27	WEB
4	2	2022-04-30 14:35:35	WEB

group by後預設是獲取第一條記錄的,

如果只想獲取group by後的某個欄位的最大值,比如說要獲取使用者最後的登入時間,那麼我們可以:

select uid, max(login_at) as last_login_at from login_log group by uid
uid	last_login_at
1	2022-04-14 14:35:27
2	2022-04-30 14:35:35

但是,我們可能會有這樣的需求:獲取使用者的最後登入時間以及登入裝置

你可能會想到:

select uid, max(login_at) as last_login_at, login_device from login_log group by uid

這樣是不行的,上面已經提及到group by後預設是會取第一條資料,所以這樣查出來的login_device將會是分組後的第一條資料,即使用者第一次登入時所使用的裝置,而不是最後登入時間所對應的登入裝置。

那麼有以下幾種

解決方案

  • 子查詢的方式(可讀性最好):
select * from login_log where id in (select max(id) from login_log group by uid)

or

select * from login_log as log1 join (select max(id) as id from login_log group by uid) as log2 where log1.id = log2.id
  • 連表的方式
select log1.* from login_log as log1
left join login_log as log2 on log1.uid = log2.uid and log1.id < log2.id
where log2.id is null
  • exists的方式
select
*
from login_log as log1
where not exists (
  select * from login_log as log2
  where log2.uid = log1.uid
  and log2.Id > log1.Id
)
  • window function的方式(mysql8)
WITH ranked_log AS (
  SELECT log.*, ROW_NUMBER() OVER (PARTITION BY uid ORDER BY id DESC) AS rn
  FROM login_log AS log
)
SELECT * FROM ranked_log WHERE rn = 1;

參考文件:StackOverflow