Question

I have two tables:

ci_categories
with fields cat_id, cat_name, cat_slug etc.

ci_albums
with fields cat_id, album_id, album_name etc.

I want to join these two tables, display category listing, and COUNT albums in each category. Like:

  • Category 1 - 20 Albums

  • Category 2 - 25 Albums

How is this done properly in PostgreSQL?

Was it helpful?

Solution

The following query should get you what you want:

SELECT c.cat_id, COUNT(a.album_id)
FROM ci_categories AS c
LEFT JOIN ci_albums AS a ON c.cat_id = a.cat_id
GROUP BY c.cat_id

We use a LEFT JOIN here due to the possibility of categories which contain no albums. If you don't care if those results show up, just change this to an INNER JOIN.

We use the aggregate COUNT(a.album_id) function to count the number of records. By grouping by the c.cat_id, we make sure this count is done only over records of the same type. If there is no album for that category, a.album_id will be NULL, and so COUNT will not include it in the total count.

OTHER TIPS

select c.cat_name, 
       count(distinct a.album_id) as albums
from ci_categories c
left join ci_albums a on a.cat_id = c.cat_id
group by c.cat_name
SELECT ci_categories.cat_name, COUNT(*) as count

FROM ci_categories JOIN ci_albums ON ci_categories.cat_id = ci_albums.cat_id

GROUP BY ci_categories.cat_name;

The other answers are somewhat equal with some slight differences, notably the DISTINCT in the COUNT and the aliasing of the tables.

EDIT / WARNING

Well, my answer is actually wrong, due to the fact that a JOIN will also include a categories if there are no albums in that category. That category is then joined to NULL values, which are counted by COUNT(*) anyways. Leaving this one here as an example of a common mistake.

If you are satisfied with cat_id and the count of albums in the result, you don't need the table ci_categories in your query at all. Use this simpler and faster query:

SELECT cat_id, count(*) AS albums
FROM   ci_albums
GROUP  BY 1;

Assuming proper table definitions without duplicate entries.

This does not includes categories with no albums at all. Only if you need those - too, or if you need additional information from the table ci_categories itself - only then you need to join to that table. But since we are counting everything, it is faster to aggregate first and join later:

SELECT c.cat_id, c.cat_name, COALESCE(a.albums, 0) AS albums
FROM   ci_categories c
LEFT   JOIN (
   SELECT cat_id, count(*) AS albums
   FROM   ci_albums
   GROUP  BY 1
   ) a USING (cat_id);

LEFT JOIN if you want categories with 0 albums, too. Else, just JOIN.
COALESCE is only needed if you need 0 instead of NULL in the result.
Test performance with EXPLAIN ANALYZE if you care.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top