Group Query of SQL Antipattern

We usually make other special requests for grouped queries, such as when I want to get the last comment of each user and the ID of the last comment of each user. It seems that this requirement is reasonable, but we can't just use aggregation functions to achieve it.
The following example is to get the last time each administrator submits code:

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

Back to that question, how do we get the ID of this last submitted code record? Usually we do it in the following way:

 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; 

It seems reasonable, but can you make sure that the record of the last code submission is the record with the largest ID? It seems that there is no guarantee that every case is the record with the largest ID and the last submission. After all, whether or not we submit the last submission depends on the field of recording time, so this method also shows a little bit of chicken ribs.

In fact, the implementation of this requirement does not necessarily need to use set functions, in fact, there are many ways to achieve.

First, I will post the table structure of the two tables that need to be used:

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);

The first way is to use subqueries

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
);

The results are as follows:

You can see that I found four pieces of data, because each administrator submitted two codes on the last day. Then we will try to get the record of the largest ID at the end of the last day.

The second way: derivative tables

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;

Don't worry. You'll understand it slowly. But we have made the record of getting the maximum ID as the last submission, but using max(c1.recode_id) is not the only choice. Look at the following way:

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;

The last method: Outline query

 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;

In fact, there are many ways to use SQL query, this is some dry goods I wrote when reviewing SQL.

Keywords: SQL

Added by 8ball on Wed, 19 Jun 2019 00:57:58 +0300