Reputation: 34272
I have a simple table for the events log:
uid | event_id | event_data
----+----------+------------
1 | 1 | whatever
2 | 2 |
1 | 3 |
4 | 4 |
4 5 |
If I need the latest event for a given user, that's obvious:
SELECT * FROM events WHERE uid=needed_uid ORDER BY event_id DESC LIMIT 1
However, suppose I need the latest events for each user id in an array. For example, for the table above and users {1, 4}
I'd expect events {3, 5}
. Is that possible in plain SQL without resorting to a pgSQL loop?
Upvotes: 2
Views: 603
Reputation: 656291
To return rows in the original order of array elements:
SELECT e.*
FROM unnest('{1, 4}'::int[]) WITH ORDINALITY a(uid, ord) -- input array here
CROSS JOIN LATERAL (
SELECT * FROM events e
WHERE e.uid = a.uid
ORDER BY e.event_id DESC
LIMIT 1
) e
ORDER BY a.ord;
Details for WITH ORDINALITY
:
There is a subtle difference to the @a_horse's query: If the given array has duplicate elements, this query gets duplicate rows in return, which may or may not be desirable. If it's not, add a DISTINCT
step after unnest()
and before the join to the big table.
The main benefit is optimized index usage. See:
Using an implicit JOIN LATERAL
:
SELECT e.*
FROM (SELECT '{1, 4}'::int[]) a(arr) -- input array here
, generate_subscripts(a.arr, 1) i
CROSS JOIN LATERAL (
SELECT * FROM event e
WHERE e.uid = a.arr[i.i]
ORDER BY e.event_id DESC
LIMIT 1
) e
ORDER BY i.i;
Upvotes: 1
Reputation:
A Postgres specific solution is to use distinct on
which is usually faster than the solution using a window function:
select distinct on (uid) uid, event_id, event_data
from events
where uid in (1,4)
order by uid, event_id DESC
Upvotes: 3
Reputation: 34272
This came to me few seconds after I posted the question. It's not as efficient, but to consider all the options:
SELECT * FROM events WHERE event_id IN
(SELECT MAX(event_id) FROM events GROUP BY uid WHERE uid IN (1,4))
Upvotes: 0
Reputation: 2664
Maybe this helps:
SELECT uid,
event_id
FROM(SELECT uid,
event_id,
ROW_NUMBER() OVER (PARTITION BY uid ORDER BY event_ID DESC) rank
FROM events
)
WHERE uid IN (1, 4)
AND rank = 1
Upvotes: 1
Reputation: 3202
try below query :
select DesiredColumnList
from
(
select *, row_number() over ( partition by uid order by event_id desc) rn
from yourtable
) t
where rn = 1
Row_Number
will assign unique number starting from 1 to each row order by event_id desc
and partition by
will ensure that numbering should be done for each group of uid
.
Upvotes: 2