I want to create a diagram based on an SQL query. The diagram should show every 12 months of the current year, even if some of them are still in the future.
My SQL query is something like this:
SELECT
MONTH (created) as m,
sum (price) as s
FROM
product
WHERE
YEAR (created) = 2020
GROUP BY
m
Of course, this only shows results for the current or past months, which also have any entries.
Is it possible
- to formulate the query so that 12 groups are displayed in any case and
- if there is no group it gets a default value of 0?
Sure, left join it to a collection of all 12 months. This is MySQL (because you used GROUP BY alias, which I think is MySQL only, maybe Postgres too):
WITH months as (
SELECT 1 as m
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
UNION ALL SELECT 6
UNION ALL SELECT 7
UNION ALL SELECT 8
UNION ALL SELECT 9
UNION ALL SELECT 10
UNION ALL SELECT 11
UNION ALL SELECT 12
)
SELECT
m.m,
sum (p.price) as s
FROM
months m
LEFT JOIN product p
ON
m.m = MONTH(p.created) AND
p.created > '2020-01-01' and p.Created < '2021-01-01'
GROUP BY
m.m
Months that don't match will have a NULL, which will sum as 0
You should try and prefer to not use functions like YEAR() in the WHERE clause because they mean that indexes can't be used; instead leave the table data alone and put a ranged search in instead
If your MySQL is old and doesn't support CTE, make that a subquery instead. If your database isn't MySQL/Pg, or it has to select from something you can make it select from a table that has only one row
An alternative trick that doesn't employ a join, would be to union a load of zero months in; they wont contribute to sums but will provide a month with 0:
SELECT
m,
sum (price) as s
FROM
(
SELECT MONTH(created) as m, price FROM product WHERE created >= '2020-01-01' and created < '2021-01-01'
UNION ALL SELECT 1, 0 UNION ALL SELECT 2, 0 UNION ALL SELECT 3, 0
UNION ALL SELECT 4, 0 UNION ALL SELECT 5, 0 UNION ALL SELECT 6, 0
UNION ALL SELECT 7, 0 UNION ALL SELECT 8, 0 UNION ALL SELECT 9, 0
UNION ALL SELECT 10, 0 UNION ALL SELECT 11, 0 UNION ALL SELECT 12, 0
) x
GROUP BY
m
If you have a table dedicated to numbers and dates, the union all block can be replaced with a call to it. If you're on Postgres, it has a series generator that can do cool things