Reputation: 4774
I have photo
table:
create table photo(
id integer,
...
user_id integer,
created_at date
);
I'd like to achieve the same result as:
select
json_agg(photo),
created_at,
id_user
from photo
group by created_at, id_user
order by created_at desc, id_user
limit 5;
but avoiding full table scan on photo
.
Is it possible? I was thinking of recursive CTE but I couldn't manage to construct it.
Upvotes: 1
Views: 958
Reputation: 125254
If there is an index on created_at
and it is possible to suppose there are at least 5 photos in the last 24 hours (or 48 or whatever) it is possible to avoid the full scan:
select
json_agg(photo),
created_at,
id_user
from photo
where created_at > (select max(created_at) from photo) - interval '24 hours'
group by created_at, id_user
order by created_at desc, id_user
limit 5;
The shorter the interval the shorter the scan.
Upvotes: 0
Reputation: 1269843
Assuming you have an index on photo(id_user, created_at)
, then you can select the five rows that you want using a subquery. Then use a join or correlated subquery to get the rest of the information:
select cu.created_at, cu.id_user,
(select json_agg(p.photo)
from photo p
where cu.created_at = p.created_at and cu.id_user = p.id_user
)
from (select distinct created_at, id_user
from photo p
order by created_at desc, id_user
limit 5
) cu
order by cu.created_at desc, cu.id_user ;
Upvotes: 4
Reputation: 48197
Not recursive, You can try with a single CTE to see if get the TOP 5 without full scan
WITH cte as (
SELECT DISTINCT created_at, id_user
FROM photo
ORDER BY created_at DESC, id_user
LIMIT 5
)
SELECT p.created_at, p.id_user, json_agg(p.photo)
FROM photo p
JOIN cte c
ON p.created_at = c.created_at
AND p.id_user = c.id_user
GROUP BY p.created_at, p.id_user
ORDER BY p.created_at DESC, p.id_user
Upvotes: 1