A fiddle for my question can be found on https://dbfiddle.uk/?rdbms=postgres_10&fiddle=3cd9335fa07565960c1837aa65143685.
I have a simple table layout:
class
person: belongs to a class
I want to select all classes, and for each class, I want the first two person identifiers of the belonging persons sorted by descending name.
I solved this with the following query:
select c.identifier, array_agg(p.identifier order by p.name desc) as persons
from class as c
left join lateral (
select p.identifier, p.name
from person as p
where p.class_identifier = c.identifier
order by p.name desc
limit 2
) as p
on true
group by c.identifier
order by c.identifier
Note: I could have used a correlation subquery in the SELECT
clause, but I am trying to avoid that as part of a learning process.
As you can see, I am applying order by p.name desc
in two places:
- in the subquery
- in the aggregate function
Is there a way to avoid that? My train of tought:
-
First, obviously I cannot remove the
order by
in the subquery, as that would give a query which does not meet my requirement as stated above. -
Second, I think that the
order by
in the aggregate function cannot be left out, as row order of the subquery is not necessarily preserved in the aggregate function?
Should I rewrite the query?
Best Answer
Yes. Aggregate with an ARRAY constructor in the lateral subquery directly:
You also don't need
GROUP BY
in the outerSELECT
this way. Shorter, cleaner, faster.I replaced the
LEFT JOIN
with a plainCROSS JOIN
since the ARRAY constructor always returns exactly 1 row. (Like you pointed out in a comment.)db<>fiddle here.
Related:
Order of rows in subqueries
To address your comment:
Well, no. While the SQL standard does not offer any guarantees, there are limited guarantees in Postgres. The manual:
If all you do in the next level is to aggregate rows, the order is positively guaranteed. Any yes, what we feed to the ARRAY constructor is a subquery, too. That's not the point. It would work with
array_agg()
as well:But I expect the ARRAY constructor to be faster for the case. See: