Create nested json from sql query postgres 9.4
You should build a hierarchical query to get a hierarchical structure as a result.
You want to have many persons in a single json object, so use json_agg()
to gather persons in a json array.
Analogically, a person can have multiple cars and you should place cars belonging to a single person in a json array. The same applies to cars and wheels.
select
json_build_object(
'persons', json_agg(
json_build_object(
'person_name', p.name,
'cars', cars
)
)
) persons
from person p
left join (
select
personid,
json_agg(
json_build_object(
'carid', c.id,
'type', c.type,
'comment', 'nice car', -- this is constant
'wheels', wheels
)
) cars
from
car c
left join (
select
carid,
json_agg(
json_build_object(
'which', w.whichone,
'serial number', w.serialnumber
)
) wheels
from wheel w
group by 1
) w on c.id = w.carid
group by personid
) c on p.id = c.personid;
The (formatted) result:
{
"persons": [
{
"person_name": "Johny",
"cars": [
{
"carid": 1,
"type": "Toyota",
"comment": "nice car",
"wheels": [
{
"which": "front",
"serial number": 11
},
{
"which": "back",
"serial number": 12
}
]
},
{
"carid": 2,
"type": "Fiat",
"comment": "nice car",
"wheels": [
{
"which": "front",
"serial number": 21
},
{
"which": "back",
"serial number": 22
}
]
}
]
},
{
"person_name": "Freddy",
"cars": [
{
"carid": 3,
"type": "Opel",
"comment": "nice car",
"wheels": [
{
"which": "front",
"serial number": 3
}
]
}
]
}
]
}
If you are not familiar with nested derived tables you may use common table expressions. This variant illustrates that the query should be built starting from the most nested object toward the highest level:
with wheels as (
select
carid,
json_agg(
json_build_object(
'which', w.whichone,
'serial number', w.serialnumber
)
) wheels
from wheel w
group by 1
),
cars as (
select
personid,
json_agg(
json_build_object(
'carid', c.id,
'type', c.type,
'comment', 'nice car', -- this is constant
'wheels', wheels
)
) cars
from car c
left join wheels w on c.id = w.carid
group by c.personid
)
select
json_build_object(
'persons', json_agg(
json_build_object(
'person_name', p.name,
'cars', cars
)
)
) persons
from person p
left join cars c on p.id = c.personid;
I've come up with this solution. It's quite compact and works in any given case.
Not sure however what the impact is on performance when comparing to other solutions which make more use of json_build_object
. The advantage of using row_to_json
over json_build_object
is that all the work is done under the hood, which makes the query more readable.
SELECT json_build_object('persons', json_agg(p)) persons
FROM (
SELECT
person.name person_name,
(
SELECT json_agg(row_to_json(c))
FROM (
SELECT
id carid,
type,
(
SELECT json_agg(row_to_json(w))
FROM (
SELECT
whichone which,
serialnumber
FROM wheel
WHERE wheel.carid = car.id
) w
) wheels
FROM car
WHERE car.personid = person.id
) c
) AS cars
FROM person
) p