I have the following schema:
expenses
| id | name, varchar | cost, double | date, DATE | category_id, int f_key | user_id, int f_key |
|---|---|---|---|---|---|
| 1 | Pizza | 22.9 | 22/08/2022 | 1 | 1 |
| 2 | Pool | 34.9 | 23/08/2022 | 2 | 1 |
categories
| id | name, varchar |
|---|---|
| 1 | Food |
| 2 | Leisure |
| 3 | Medicine |
| 4 | Fancy food |
users_categories(user_id int foreign key, category_id foreign key)
| user_id int f_key | category_id int f_key |
|---|---|
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 4 |
And two users with id 1 and 2.
Relation between user and category is many to many.
Problem:
I want to get statistics (total cost amount and count) for all categories. For categories where there are no expenses I want to return 0. Here is my query:
SELECT categories.name as name, count(expenses.name) as count, round(SUM(price)::numeric,2) as sum
FROM expenses
Right JOIN categories ON expenses.category_id = categories.id
and expenses.category_id in (
select users_categories.category_id from users_categories where users_categories.user_id = 1
)
and expenses.id in(
Select expenses.id from expenses
join users_categories on expenses.category_id = users_categories.category_id
and expenses.user_id = 1
AND (extract(year from date) = 2022 OR CAST(2022 AS int) is null)
AND (extract(month from date) = 8 OR CAST(8 AS int) is null)
)
GROUP BY categories.id ORDER BY categories.id
The response is:
| name | count | sum |
|---|---|---|
| Food | 1 | 22.9 |
| Leisure | 1 | 33.9 |
| Medicine | 0 | null |
| Fancy food | 0 | null |
How I should edit my query to eliminate the last row, because this category doesn't belong to the user 1.
CodePudding user response:
You want to move expenses.category_id in ... out of the ON condition and into a WHERE clause.
When it is in the ON clause, that means rows which were removed by the in-test just get NULL-fabricated anyway. You want to remove those rows after the NULL-fabrication is done, so that they remain removed. But why do you use that in-test anyway? Seems like it would be much simpler written as another join.
CodePudding user response:
What I understood is you are trying to get the count and sum of expenses for all the categories related to the user_id 1 within the month of august 2022.
Please try out the following query.
WITH statistics AS (SELECT e.category_id, count(e.*) as count, round(sum(e.cost),2) as sum FROM expenses e WHERE e.user_id=1 AND (e.date BETWEEN '01/08/2022' AND '31/08/2022') GROUP BY e.category_id), user_category as(SELECT uc.category_id, COALESCE(s.count,0) as count, COALESCE(s.sum,0) as sum FROM users_categories uc LEFT JOIN statistics s ON uc.category_id=s.id WHERE uc.user_id=1) SELECT c.name, u.count, u.sum FROM categories c INNER JOIN user_category u ON u.category_id=c.id;
