Get data from different tables indexed by same ID
-
28-02-2021 - |
Question
Database: postgresql 12. I have a table people
:
person_id | name
----------+-----
1 |John
2 |Paul
3 |Jim
...
Each person can have zero or more car insurance contracts, so I have another table car_contracts
as follows:
contract_id | person_id | contract_number | date_from | insured_value | car_tag | .... (more fields)
------------+-----------+-----------------+-----------+---------------+---------+
1 | 1 | C99 |2020-01-01 | 5000 | ABC | ....
2 | 1 | C02 |2020-03-01 | 4000 | XYZ | ....
3 | 3 | C96 |2020-02-02 | 12000 | KKK | ....
4 | 1 | C01 |2019-01-01 | 5500 | XXX | ....
5 | 2 | C02 |2019-06-01 | 1000 | 666 | ....
6 | 3 | C03 |2020-02-20 | 11000 | KPX | ....
...
Each person can also have zero or more power plants (or whatever; the point is, another table with different fields but still tied to people by means of person_id
), so the table plants
is like
plant_id | person_id | plant_name | power | address | .... (more fields)
---------+-----------+------------+-------+-------------+
1 | 2 | plant01 |100 | 230 rue st. | ....
2 | 3 | jimsplant1 |50 | ... | ....
3 | 3 | jimsplant2 |65.5 | | ....
4 | 2 | plant02 |12.5 | | ....
5 | 1 | house01 |20 | | ....
6 | 1 | house02 |20 | | ....
...
I can either get a list of people with their contracts or people with their plants by joining the people
table to the car_contracts
and plants
table respectively.
Is it possible to somehow get all the information at once (people with their contracts and their plants) with a single query?
The main issue seems to me that the number of contracts and the number of plants have no relation and can be very different, so I think joining people with contracts with plants makes little sense.
Solution
Well, you can aggregate the rows into arrays:
select p.person_id, p.name,
array_agg(cc) as contracts,
array_agg(pl) as plants
from people p
left join car_contracts cc on p.person_id = cc.person_id
left join plants pl on p.person_id = pl.person_id
group by p.person_id;
(This assumes that person_id
is declared as the primary key)
Aggregating the rows into json values might be easier to work with in the front end:
select p.person_id, p.name,
json_agg(to_jsonb(cc)) as contracts,
jsong_agg(to_jsonb(pl)) as plants
from people p
left join car_contracts cc on p.person_id = cc.person_id
left join plants pl on p.person_id = pl.person_id
group by p.person_id;
To avoid duplicates, do the aggregation in a derived table:
select p.person_id, p.name,
cc.contracts,
pl.plants
from people p
left join (
select person_id, jsonb_agg(to_jsonb(c)) as contracts
from car_contracts c
group by person_id
) cc on p.person_id = cc.person_id
left join (
select person_id, jsonb_agg(to_jsonb(p)
from plants
group by person_id
) pl on p.person_id = pl.person_id;