I have 3 tables A, B, C.
- A is the main table
- B & C have a many-to-one relation with A
Let's say the data is as follow:
Table A Table B Table C
| id | name | | id | a_id (FK) | value | | id | a_id (FK) | value |
| :---: | :---: | | :---: | :-------: | :---: | | :---: | :-------: | :---: |
| a0 | A0 | | b0 | a0 | B0 | | c0 | a0 | C0 |
| a1 | A1 | | b1 | a0 | B1 | | c1 | a0 | C1 |
| b2 | a1 | B2 |
I want to retrieve the records in A with their relations aggregated into an array or a json, like so:
| id | name | b | c |
|---|---|---|---|
| a0 | A0 | [[b0, B0], [b1, B1]] | [[c0, C0], [c1, C1]] |
| a1 | A1 | [[b2, B2]] | [] |
If I use the classic TypeORM .find():
const items = await aRepository.find({ relations: ["B", "C"] });
I will quickly run out of memory because TypeORM is simply making a query with LEFT JOINs which will return duplicated lines before aggregating them. There's an issue about it here.
This leaves me writing a custom SQL statement with nested SELECTs:
SELECT
A.id, A.name,
(
SELECT json_agg(bs)
FROM (
SELECT id, "value"
FROM B WHERE B.a_id = A.id
) bs
) AS b,
(
SELECT json_agg(cs)
FROM (
SELECT id, "value"
FROM C WHERE C.c_id = A.id
) cs
) AS c
FROM A
Without an index on B.a_id and C.a_id, it takes ~230ms with a 200 records in A and +/-10 records in B and C for each A record.
By creating an index on B.a_id and C.a_id, I can bring this down to 30ms.
Is there still a better way to do this?