Mark
Mark

Reputation: 1986

How can I get several counts of a column in one SQL query?

I have a schema like:

PetOwner:
owner_id | first_name | last_name
---------------------------------
1        | john       | smith
2        | jane       | doe
3        | jack       | jones    

Pets:
pet_id | owner_id | name | type
-------------------------------
1      | 1        | a    | dog
2      | 1        | b    | dog
3      | 1        | c    | dog
4      | 1        | d    | cat
5      | 1        | e    | cat
6      | 1        | f    | fish
7      | 2        | g    | dog
8      | 2        | h    | dog
9      | 2        | i    | fish
10     | 3        | j    | dog

And I'd like to write a query that gives me:

first_name | last_name | dog_count | cat_count | fish_count
-----------------------------------------------------------
john       | smith     | 3         | 2         | 1
jane       | doe       | 2         | 0         | 1
jack       | jones     | 1         | 0         | 0

There is a known set of pet types, so this doesn't need to make a column for arbitrary types. In addition, any owners that don't have a certain type of pet should get a 0 count for that pet type.

I've already tried a query like:

SELECT first_name, last_name, owner_id, type, COUNT(*)
FROM Pets, PetsOwner
GROUP_BY owner_id, type

which yields:

first_name | last_name | owner_id | type | COUNT(*)
---------------------------------------------------
john       | smith     | 1        | dog  | 3
john       | smith     | 1        | cat  | 2
john       | smith     | 1        | fish | 1
jane       | doe       | 2        | dog  | 2
jane       | doe       | 2        | fish | 1
jack       | jones     | 3        | dog  | 1

But this requires iterating over the result set to get the output I'm looking for, obviously something to avoid with SQL. Also it doesn't provide the default 0 value for pet types that are absent from the table.

Is it possible to do this with one SQL query? If not, is there a query I could do that is better (i.e. easier to work with) than the query I've found?

It's a MySQL database, if that makes a difference.

Thanks for the help.

Upvotes: 0

Views: 120

Answers (1)

PinnyM
PinnyM

Reputation: 35533

Use SUM instead of COUNT:

SELECT o.first_name, o.last_name, 
       SUM(CASE WHEN p.type = 'cat' THEN 1 ELSE 0 END) as cat_count,
       SUM(CASE WHEN p.type = 'dog' THEN 1 ELSE 0 END) as dog_count,
       SUM(CASE WHEN p.type = 'fish' THEN 1 ELSE 0 END) as fish_count
FROM PetOwners o
JOIN Pets p ON o.id = p.owner_id
GROUP BY o.id

And if you want to include owners that don't have any pets at all, change JOIN to LEFT JOIN.

Upvotes: 2

Related Questions