SELECT f.id, f.name, b.fb_ct, t.tag_names
FROM foo f
LEFT JOIN (
SELECT foo_id AS id, count(*) AS fb_ct
FROM foo_bar
GROUP BY 1
) b USING (id)
LEFT JOIN (
SELECT target_id AS id, array_agg(name) AS tag_names
FROM tag
GROUP BY 1
) t USING (id)
ORDER BY f.id;
Produces the desired result.
Rewrite with explicit
JOIN
syntax. Makes it so much easier to read and understand (and debug).By joining to multiple
1:n
related tables, rows would multiply each other producing a Cartesian product - which is very expensive nonsense. It's an unintendedCROSS JOIN
by proxy. Related:To avoid this, join at most one
n
-table to the1
-table before you aggregate (GROUP BY
). You could aggregate two times, but it's cleaner and faster to aggregaten
-tables separately before joining them to the1
-table.As opposed to your original (with implicit
INNER JOIN
). I useLEFT JOIN
to avoid losing rows fromfoo
that have no matching row infoo_bar
ortag
.Once the unintended
CROSS JOIN
is removed from the query, there is no need for addingDISTINCT
any more - assuming thatfoo.id
is unique.