views:

162

answers:

3

I have a query like this:

SELECT type.id, type.name, COUNT(*) AS tot
FROM page
LEFT JOIN type ON page.type=type.id
GROUP BY type.id

However, this doesn't select all the types: it misses out any types that are not in the page table yet. I just want it to list every type with a number representing how many pages have that type, including 0 where the type doesn't occur. Do I need a different join?

A: 

USE Coalesce to give NULL a value to GROUP BY

SELECT
    COALESCE(type.id, 0),
    COALESCE(type.name, 'NoType'),
    COUNT(*) AS tot
FROM
    page
LEFT JOIN
    type
ON
    page.type=type.id
GROUP BY
    COALESCE(type.id, 0),
    COALESCE(type.name, 'NoType')
Robin Day
+1  A: 

A left join brings back all the rows from the table on the left side of the join expression regardless if they exist on the table on the right side try this (switching the sides of the expression):

SELECT type.id, type.name, COUNT(*) AS tot
FROM  type 
LEFT JOIN  page ON type.id=page.type
GROUP BY type.id, type.name
Gratzy
+1  A: 

Do a RIGHT JOIN instead. Try this:

SELECT type.id, type.name, COUNT(page.type) AS tot
FROM page
RIGHT JOIN type ON page.type=type.id
GROUP BY type.id

Note the different way of counting. page.type will be NULL for types that don't have pages. Such rows will be ignored when doing the COUNT

[EDIT] Here is an example. If we have following data:

  type
id  name
 1     A
 2     B
 3     C

  page
id   type
 1     1
 2     1
 3     2

If we do the JOIN like in my answer (no GROUP BY or COUNT), our result set will look like this:

type.id  type.name  page.id  page.type
      1          A        1          1
      1          A        2          1
      2          B        3          2
      3          C     NULL       NULL

Now when we do a GROUP BY and COUNT(type.id), we are counting rows where type.id is not NULL. That would be all rows: result is 2 for A and 1 for B and C.

If we do COUNT(page.type) instead, we get 2 for A, 1 for B and 0 for C (because page.type was NULL for C!)

hrnt
I tried this using count(*) before reading your answer properly, and the missing type returned 2 as the count. Using count(page.type) like you said gives 0. Why does this happen?
DisgruntledGoat
It seems that using count(type.id) returns 2 as well.
DisgruntledGoat
`COUNT(*)` counts all rows. `COUNT(page.type)` only counts rows where page.type is not NULL. When you do a JOIN like in my answer, rows for `types` that do not have `pages` have all `page` columns set to NULL. When we do `COUNT(page.type)`, we are only counting rows of `types` that have `pages` (page columns are not null).
hrnt
If you do `COUNT(type.id)`, then you are counting all rows whose `type.id` is not NULL. Because we are selecting all types, they all have id's. Try SELECT * to see what the query result looks like if my explanation is unclear.
hrnt
Hmm, still not getting it. I don't have any NULLs in the type tables, nor the type field of the page table. All the other types have the correct counts but this one without a corresponding page shows 2.
DisgruntledGoat
Hmm, if I count `type.id` and group by `type.id` there are actually 2 different types not assigned to a page, each with a count of 1. But if I count `type.id` and group by `page.type` I get one of those types listed with a count of 2. But then it means it's not grouping by the type, surely?
DisgruntledGoat
I added an explanation. Hopefully that clarifies things.
hrnt
Thanks, I think I got it all sorted in my head! I set up that simple example locally, with an extra unused type (D). If you group by `page.type` the count comes up as 2 because it appears to group the NULLs (of which there are two - types C and D). In short, we should always group by the thing we're counting!
DisgruntledGoat