SQL Antipattern 之 分組查詢

我們通常爲分組查詢提出其他特別的要求,例如我想獲得每個用戶最後一次評論的時間,以及每個用戶最後一次的評論的ID。看上去好像這個要求和合理,但是我們卻無法只單單用聚合函數去實現。
以下這個例子是獲得各個管理員最後一次提交代碼的時間:

 select a.username,max(c.commit_date) from commit_recode c 
 join admins a  using(admin_id) group by a.admin_id; 

回到剛剛那個問題,我們如何去獲得這個最後一次提交的代碼記錄的ID呢?通常我們會用如下的方式去做:

 select a.username,max(c.commit_date),max(c.recode_id) 
 from commit_recode c join admins a  using(admin_id) group by a.admin_id; 

貌似很合理,但是你能確保最後一次提交代碼的記錄就是ID最大的記錄嗎?貌似也不太能保證,每一種情況都是ID最大就是最後一條提交的記錄,畢竟是否爲最後一條提交我們還是要看記錄時間的字段,所以這種方式也略顯雞肋。

其實實現這種需求不一定要用集合函數,其實有非常多的方式方法去實現。

首先,我先貼出一下需要使用到的兩張表的表結構:

insert into admins(username,userpwd,image_path) 
values
('TONY','PWD','abc.jpg'),
('CHAO','PWD',NULL),
('ADMIN','PWD','admin.jpg'),
('YAN','PWD','YAN.jpg');

create table commit_recode(recode_id int primary key auto_increment,recode_text varchar(100),commit_date date,admin_id int,
foreign key (admin_id) references admins(admin_id));

insert into commit_recode(recode_text,commit_date,admin_id)
values
('ADMIN: FIRST COMMIT CODE!','2017-1-8',3),
('TONY: FIRST COMMIT CODE!','2017-1-5',1),
('ADMIN: LAST COMMIT CODE!','2017-8-8',3),
('TONY: LAST COMMIT CODE!','2017-3-5',1),
('ADMIN: ONE MORE COMMIT CODE!','2017-8-8',3),
('TONY: ONE MORE COMMIT CODE!','2017-3-5',1);

第一種方式:使用子查詢

select a1.username,c1.commit_date,c1.recode_id from commit_recode c1 
join admins a1 using(admin_id) 
where not exists (
select * from commit_recode c2 join admins a2 using(admin_id)
 where a1.admin_id = a2.admin_id and c1.commit_date < c2.commit_date
);

查詢結果如下:
子查詢結果
可以看到我查出來4條數據,因爲每個管理員最後一天都提交了兩次代碼。之後我們會嘗試獲得最後一天最後ID最大的記錄。

第二種方式:衍生表

select a1.username,c1.commit_date,max(c1.recode_id) from commit_recode c1 
join admins a1 on c1.admin_id = a1.admin_id 
left join (
commit_recode c2 join admins a2 on c2.admin_id = a2.admin_id ) on 
( (c1.admin_id = c2.admin_id and  c2.commit_date > c1.commit_date) )
where c2.recode_id is null group by a1.username,c1.commit_date;

不着急,慢慢看總會看懂的。但是這裏已經做獲得最大ID作爲最後一次提交的記錄,但是使用max(c1.recode_id)並不是唯一的選擇,看看以下這個方式:

select a1.username,c1.commit_date,c1.recode_id from 
commit_recode c1 join admins a1 on c1.admin_id = a1.admin_id 
left join (commit_recode c2 join admins a2 on c2.admin_id = a2.admin_id ) on 
(
    (c1.admin_id = c2.admin_id and  c2.commit_date > c1.commit_date) 
or
    (c1.admin_id = c2.admin_id and 
    c2.commit_date = c1.commit_date and 
    c1.recode_id  < c2.recode_id)
 )
  where c2.recode_id is null;

這裏寫圖片描述

最後一種方法:外聯查詢

 select m.username,m.last_commit_date,max(c1.recode_id) as recode_id 
 from commit_recode c1 join admins a1 using(admin_id) JOIN (
select a2.username username,max(c2.commit_date) as last_commit_date 
from commit_recode c2 join admins a2 using(admin_id) group by a2.admin_id
 ) as m  
 on m.last_commit_date = c1.commit_date group by m.username,m.last_commit_date;

其實使用SQL查詢有非常多的方法,這個是我在複習SQL的時候寫的一些乾貨。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章