I have an items
table
id | name |
---|---|
1 | Nganu |
2 | Kae |
3 | Lho |
Also I have an item_usages
table:
id | item_id | user_id | usage_time | |
---|---|---|---|---|
1 | 1 | 99 | 2021-10-07 00:00:00 | |
2 | 2 | 99 | 2021-10-07 00:00:00 | |
3 | 1 | 99 | 2021-10-08 00:00:00 | |
4 | 1 | 22 | 2021-10-08 00:00:00 | |
5 | 3 | 22 | 2021-10-08 00:00:00 | |
6 | 1 | 99 | 2021-10-08 00:00:00 |
I want to find an item's total usage and user usage in a query. an example I would like to find user_id 99 usage, expected result:
id | name | total_usage | user_usage |
---|---|---|---|
2 | Kae | 1 | 1 |
1 | Nganu | 4 | 3 |
3 | Lho | 1 | 0 |
I tried:
select
"items".*,
count(total_usage.id) as total_usage,
count(user_usage.id) as user_usage
from
"items"
left join
"item_usages" as "total_usage" on "items"."id" = "total_usage"."item_id"
left join
"item_usages" as "user_usage" on "user_usage"."item_id" = "items"."id"
and "user_usage"."user_id" = 99
group by
"items"."id";
but it returns:
id | name | total_usage | user_usage |
---|---|---|---|
2 | Kae | 1 | 1 |
1 | Nganu | 12 | 12 |
3 | Lho | 1 | 0 |
item_usages
only have 6 rows, why Nganu have 12 on both usage? How to fix my query?
I tried on PostgreSQL 12.8 and 13.4, I also tested on SQLFiddle(PostgreSQL 9.6), Here is the link:
http://sqlfiddle.com/#!17/f1aac/5
I got the query that returned the correct result:
select
"items".*,
min(total_usage.total_count) as total_usage,
count(user_usage.id) as user_usage
from "items"
left join
(select item_id,count(item_id) as total_count from item_usages group by item_id) as total_usage
on "items"."id" = "total_usage"."item_id"
left join "item_usages" as "user_usage"
on "user_usage"."item_id" = "items"."id" and "user_usage"."user_id" = 99
group by "items"."id";
But I don't know about the performance, so I still find faster query if possible and still wondering:
Why does my first query give wrong result?
CodePudding user response:
The reason your query returns high numbers is that you join 2 times.
(From the side of Nganu) The first join will result in 4 rows, the second will map those 4 rows with 3 rows of the same table, resulting in 12 rows.
You can solve this problem with only 1 join:
select "items".id,
count(total_usage.id) as total_usage,
sum(case when total_usage.user_id = 99 then 1 else 0 end) as user_usage
from "items"
left join "item_usages" as "total_usage" on "items"."id" = "total_usage"."item_id"
group by "items".id
And it should work faster (though, on a small dataset is not visible)