I have tables level_one_table
, level_two_table
, and level_three_table
.
level_one_table
to level_two_table
: one to multiple rows
level_two_table
to level_three_table
: one to one row
Goal: select them all and level_two_table
rows able to use LIMIT
and ORDER BY
. Return data structure like below:
{
"level_one_table": {
"id": ..,
"... all other field in level_one_table"
"level_two_table": [{
"id": ..,
"... all other field in level_two_table",
"level_three_table": {
"id": ..,
"... all other field in level_three_table"
}
}]
}
}
level_one_table
:
"id" | … |
---|---|
1 | … |
2 | … |
level_two_table
:
"id" | "fk_level_one_id" | … |
---|---|---|
1 | 1 | … |
2 | 1 | … |
level_three_table
:
"id" | "fk_level_two_id" | … |
---|---|---|
1 | 1 | … |
2 | 2 | … |
SELECT
json_build_object(
'level_one_table', json_build_object(
'id', t0.id,
'level_two_table', json_arrayagg(json_build_object(
'id', t1.id,
// ... other t1 columns
// ORDER BY t1.column DESC LIMIT 5
'level_three_table', json_build_object(
'id', t2.id
// ... other t2 columns
)
))
)
)
FROM level_one_table t0
LEFT JOIN level_two_table t1 ON t0.id = t1.fk_level_one_id
LEFT JOIN level_three_table t2 ON t1.id = t2.fk_level_two_id
GROUP BY t0.id
// ORDER BY t0.column ... DESC LIMIT 10
Is there a way to add LIMIT
or ORDER BY
inside json_arrayagg()
?
Or any other solution get the same result?
2
Answers
You can perform the
ORDER BY
within thejson_arrayagg
, but you will need to do theLIMIT
operation within the query onlevel_two_table
by computing a row number for eachfk_level_one_id
and then using aWHERE
clause at the top-level to only select the first n rows for each group.I’ve expanded the demo from the answer to your previous question to demonstrate this in operation here.
Do the aggregation in a
LATERAL
subquery. Then you can add plainORDER BY
&LIMIT
before you aggregate:fiddle
Notably, I synced table aliases with table names (table names 1-based, but aliases 0-based wasn’t helpful).
Here,
json_agg()
can replace the more sophisticatedjson_arrayagg()
(added in Postgres 16) to aggregate the JSON array.Result:
You get a dummy entry
"level_two_table": null
if there are no related rows. Strip all null values withjson_strip_nulls()
, or useCASE
or similar to strip electively.Related:
For a dynamic number of nested levels: