I'm pretty sure your problem is that you're grouping by products.department_id
instead of departments.department_id
. If a department has no products, then the left join will produce (before grouping) a single result row for that department with all the products.*
columns set to NULL. If you group by one of these columns — even the department ID — then the grouped result will merge all departments with no products together, since products.department_id
will be NULL for all of them.
Here's a simple example to demonstrate this:
CREATE TEMPORARY TABLE foo (
foo_id INTEGER PRIMARY KEY
);
CREATE TEMPORARY TABLE bar (
bar_id INTEGER PRIMARY KEY,
foo_id INTEGER NULL
);
INSERT INTO foo (foo_id) VALUES (10), (20), (30), (40), (50);
INSERT INTO bar (bar_id, foo_id) VALUES
(1, 10), (2, 10), (3, 10),
(4, 20), (5, 20), (6, 30);
SELECT foo.foo_id, COUNT(bar.bar_id)
FROM foo
LEFT JOIN bar USING(foo_id)
GROUP BY bar.foo_id
If you run this query, your results should look something like this:
foo.foo_id COUNT(bar.bar_id)
40 0
10 3
20 2
30 1
Uh, wait... what happened to foo_id
50? Well, what happened is that, before the grouping, the left join produced a result set like this:
foo.foo_id bar.foo_id bar.bar_id
10 10 1
10 10 2
10 10 3
20 20 4
20 20 5
30 30 6
40 NULL NULL
50 NULL NULL
Notice how there are two rows where bar.foo_id
is NULL. If you then group the results on that column, those rows will get grouped together, even though they do have a different value for foo.foo_id
.
While a stricter RDBMS might then complain that you're selecting the non-grouped column foo.foo_id
without wrapping it in an aggregate function like COUNT()
or SUM()
, MySQL isn't that strict by default and will just pick an arbitrary value for that column from each group. In the example above, I got 40, but it could just as well have been 50.
Anyway, the fix is simple: just change the grouping column to departments.department_id
and you'll get the results you expect. Or, to keep using my example schema, I can change the grouping column in the query above from bar.foo_id
to foo.foo_id
and get the following results:
foo.foo_id COUNT(bar.bar_id)
10 3
20 2
30 1
40 0
50 0