Reputation: 214
This is related to Get records with max value for each group of grouped SQL results, except that the necessary groups are in a separate table.
Let's say that I have a couple of buildings, each building has some offices, and I have some people who "check in" to those offices to do work.
This table, called "offices", matches buildings to offices:
building office
---
Seuss Yertle
Seuss Cubbins
Milne Pooh
Milne Eeyore
Milne Roo
This table, called "checkins", records when people worked in each office:
id office person timestamp
---
1 Yertle Aaron 100
2 Cubbins Aaron 200
3 Pooh Aaron 300
4 Cubbins Charlie 300
5 Cubbins Aaron 700
6 Eeyore Beth 600
7 Pooh Beth 400
I'd like to generate a table that tells me, for each building-person combo, which check-in was the most recent one for that person in that building:
building person checkin_id office timestamp
---
Seuss Aaron 5 Cubbins 700
Milne Aaron 3 Pooh 300
Milne Beth 6 Eeyore 600
Seuss Charlie 4 Cubbins 300
I'm at a loss for how do I do this. The standard trick involves joining a table to itself while comparing the relevant value, then throwing away the rows where there is no bigger value. I assume I'll need two copies of "checkins" and two copies of "buildings" with a complex join between them, but I can't seem to get the NULLs to show up in the correct place.
I'm using MySQL, if that helps.
Upvotes: 0
Views: 89
Reputation: 31993
use corelated subquery
select b.* from
(select o.building,c.person,c.id as checkinid,
c.office,c.timestamp from
offices o join checkins c
on o.office=c.office
) b
where b.timestamp = (select max(a.timestamp)
from (
select o.building,c.person,
c.office,c.timestamp from
offices o join checkins c
on o.office=c.office
) as a where a.building=b.building and
a.person=b.person
)
order by person
output
building person checkinid office timestamp
Milne Aaron 3 Pooh 300
Seuss Aaron 5 Cubbins 700
Milne Beth 6 Eeyore 600
Seuss Charlie 4 Cubbins 300
Upvotes: 0
Reputation: 1269803
There is a trick using group_concat()
and substring_index()
that allows you to do this with a single group by
:
select o.building, c.person,
max(c.id) as checkinid,
substring_index(group_concat(c.office order by timestamp desc), ',', 1) as office,
max(c.timestamp) as timestamp
from offices o join
checkins c
on o.office = c.office
group by o.building, c.person;
This version assumes that id
and timestamp
increase together, so max()
can be used for both.
Also, group_concat()
-- by default -- has a limit of about 1,000 characters for the intermediate result, so this won't work if there are lots and lots of offices for a person/building combination or if the offices have long names. Of course, the separator can be changed if commas appear in the office
name.
Upvotes: 1
Reputation: 147166
To get the result you want, you have to create a table which has all checkins in each office (JOIN
offices
to checkins
) and then JOIN
that to a table of the latest checkin for each person in each building:
SELECT o.building, c.person, c.id AS checkin_id, c.office, c.timestamp
FROM offices o
JOIN checkins c ON c.office = o.office
JOIN (SELECT o.building, c.person, MAX(c.timestamp) AS timestamp
FROM offices o
JOIN checkins c ON c.office = o.office
GROUP BY o.building, c.person) t ON t.building = o.building AND t.person = c.person AND t.timestamp = c.timestamp
ORDER BY c.person, c.office
Output:
building person checkin_id office timestamp
Seuss Aaron 5 Cubbins 700
Milne Aaron 3 Pooh 300
Milne Beth 6 Eeyore 600
Seuss Charlie 4 Cubbins 300
Upvotes: 0