Reputation:
I have three tables: users
, items
, and boards
.
create table users (
id serial primary key,
name text
);
create table items (
id serial primary key,
user_id int,
title text
);
create table boards (
id serial primary key,
user_id int,
title text
);n
I want to select all the users with the number of items and boards each user has:
select
users.*,
count(items) as item_count,
count(boards) as board_count
from users
left join items on items.user_id = users.id
left join boards on boards.user_id = users.id
group by users.id;
Unfortunately, this does not give me the right counts. The counts are way higher than they should be. What is happening, why, and how can I fix this?
Upvotes: 8
Views: 4445
Reputation: 92785
The problem is caused by fact that two left joins produce duplicate values.
To solve this you can either count only distinct values
SELECT u.id, u.name,
COUNT(DISTINCT i.id) item_count,
COUNT(DISTINCT b.id) board_count
FROM users u LEFT JOIN items i
ON u.id = i.user_id LEFT JOIN boards b
ON u.id = b.user_id
GROUP BY u.id, u.name
or count items and boards per user separately and then join them with users
SELECT u.*,
COALESCE(i.item_count, 0) item_count,
COALESCE(b.board_count, 0) board_count
FROM users u LEFT JOIN
(
SELECT user_id, COUNT(*) item_count
FROM items
GROUP BY user_id
) i ON u.id = i.user_id LEFT JOIN
(
SELECT user_id, COUNT(*) board_count
FROM boards
GROUP BY user_id
) b ON u.id = b.user_id
Here is SQLFiddle demo
Upvotes: 11
Reputation: 9052
I think this should do the trick
select
u.*,
(select count(*) from items where user_id = u.id) as item_count,
(select count(*) from boards where user_id = u.id) as board_count
from users as u
group by u.id;
Upvotes: 3