Vaibhav Pandey Vaibhav Pandey - 1 month ago 15
MySQL Question

LEFT JOIN returns 1st row from left table only if right table is empty

I have two tables with following structure.

CREATE TABLE `countries` (
`id` BIGINT UNSIGNED AUTO_INCREMENT,
`title` VARCHAR (128) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE = InnoDB;

CREATE TABLE `cities` (
`id` BIGINT UNSIGNED AUTO_INCREMENT,
`country_id` BIGINT UNSIGNED NOT NULL,
`title` VARCHAR (128) NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`country_id`) REFERENCES `countries` (`id`) ON DELETE CASCADE
) ENGINE = InnoDB;


Then I add 3 rows to the countries table:

INSERT INTO `countries` VALUES
(DEFAULT, 'India'),
(DEFAULT, 'Pakistan'),
(DEFAULT, 'Afghanistan');


Now, when I run the following query:

SELECT c.title, COUNT(c2.id) AS cities
FROM countries AS c
LEFT JOIN cities AS c2
ON c2.country_id = c.id
GROUP BY c2.country_id;


I get:

++++++++++++++++++++++++
| title | cities |
++++++++++++++++++++++++
| India | 0 |
++++++++++++++++++++++++


What I expect is:

++++++++++++++++++++++++
| title | cities |
++++++++++++++++++++++++
| India | 0 |
| Pakistan | 0 |
| Afghanistan | 0 |
++++++++++++++++++++++++


I do not know why the query is returning only 1 row from the left table. Can anybody please help?

Answer

Since you want result for all the entries of countries table irrespective of whether the corresponding records in cities table exist or not then LEFT JOIN is the right choice.

For some countries corresponding entries in cities table might not exist. In this case you will get NULL for those.

Since no entries exist in cities table you will get NULL for c2.country_id for all the rows of countries table.

Here NULL represents your group.

If there were anything other than null then you would get more than 1 row. But in your case you are grouping by NULL.

Look, c.id isn't null in those cases and if you would group by c.id then there would be three distinct ids in your sample input. So, you would get three rows in your result set.

SELECT c.title, COUNT(c2.id) AS cities
FROM countries AS c
  LEFT JOIN cities AS c2
    ON c2.country_id = c.id
GROUP BY c.id;

More:

Some subtleties regarding COUNT:

SELECT COUNT(0);   Result: 1

SELECT COUNT(-1);  Result: 1

SELECT COUNT(NULL); Result: 0

SELECT COUNT(71); Result: 1

SQL FIDDLE