Displaying zero valued months with SQL
While not impossible, it's not easy to do this in SQL.
Since it is mostly a presentation issue it would probably be easier to add the missing rows in the client after you have executed the query in your question by iterating over the results and checking for missing months.
SQL How to show '0' value for a month, if no data exists in the table for that month
You can query a table with the values 1-12 and left outer join your result.
Here is a sample using a table variable instead of your query and a CTE to build a table with numbers.
declare @T table
(
Month int
)
insert into @T values(1)
insert into @T values(1)
insert into @T values(1)
insert into @T values(3)
insert into @T values(3)
;with Months(Month) as
(
select 1
union all
select Month + 1
from Months
where Month < 12
)
select M.Month,
count(T.Month) Count,
isnull(sum(T.Month), 0) Sum
from Months as M
left outer join @T as T
on M.Month = T.Month
group by M.Month
Result:
Month Count Sum
----------- ----------- -----------
1 3 3
2 0 0
3 2 6
4 0 0
5 0 0
6 0 0
7 0 0
8 0 0
9 0 0
10 0 0
11 0 0
12 0 0
How to show months if it has no record and force it to zero if null on MySQL
Thanks all for the answers & comments i really appreciate it.
i solved it by create table helper for static months then use union and aliasing, since i need the months in indonesia, i create case-when function too.
so, the query is like this:
SELECT total_orders,
(CASE date WHEN 01 THEN 'Januari'
WHEN 02 THEN 'Februari'
WHEN 03 THEN 'Maret'
WHEN 04 THEN 'April'
WHEN 05 THEN 'Mei'
WHEN 06 THEN 'Juni'
WHEN 07 THEN 'Juli'
WHEN 08 THEN 'Agustus'
WHEN 09 THEN 'September'
WHEN 10 THEN 'Oktober'
WHEN 11 THEN 'November'
WHEN 12 THEN 'Desember'
ELSE date END ) AS date
FROM (SELECT SUM(total) AS total_orders,
DATE_FORMAT(created_at, "%m") AS date
FROM orders
WHERE is_active = 1
AND tenant_id = 2
AND created_at like '%2021%'
GROUP BY DATE_FORMAT(created_at, "%m")
UNION
SELECT 0 AS total_orders,
code AS date
FROM quantum_default_months ) as Q
GROUP BY date
I still don't know if this query is fully correct or not, but I get my exact result.
cmiiw.
thanks all
How to group by month and return zero if no value for certain month?
Maybe this it's not the best way to do it, but it will solve your problem. As a quick soution:
SELECT 'January' AS mName, 1 AS mOrder, COALESCE(SUM(amount),0) AS total_num
FROM income i
WHERE month(i.date) = 1
UNION
SELECT 'February' AS mName, 2 AS mOrder, COALESCE(SUM(amount),0) AS total_num
FROM income i
WHERE month(i.date) = 2
UNION
...and go on
Show Each Month And Count Or 0 If Null
You can simply add a lookup table with month that will contains 12 records (one for each month)
The structure will be: id, description.
On existing tables you can refers month by id: less space more performance in join.
On your query using left/right join you will always have the missing month.
PS: need only one table to manage this, and adding a simple where condition (or group by condition) where needed for years management
Get mySQL MONTH() to use leading zeros?
Use the following instead:
DATE_FORMAT(`datetime_added`,'%Y-%m')
Explanation:
DATE_FORMAT()
function lets you format the date anyway you like using specifiers described in the table below (taken verbatim from documentation). So a format string '%Y-%m'
means: "A full year (4 digits), followed by a dash (-
), followed by a two-digit month number".
Note that you can specify the language used for day/month names by setting lc_time_names
system variable. Extremely useful. Refer to documentation for more details.
Specifier | Description |
---|---|
%a | Abbreviated weekday name (Sun..Sat) |
%b | Abbreviated month name (Jan..Dec) |
%c | Month, numeric (0..12) |
%D | Day of the month with English suffix (0th, 1st, 2nd, 3rd, …) |
%d | Day of the month, numeric (00..31) |
%e | Day of the month, numeric (0..31) |
%f | Microseconds (000000..999999) |
%H | Hour (00..23) |
%h | Hour (01..12) |
%I | Hour (01..12) |
%i | Minutes, numeric (00..59) |
%j | Day of year (001..366) |
%k | Hour (0..23) |
%l | Hour (1..12) |
%M | Month name (January..December) |
%m | Month, numeric (00..12) |
%p | AM or PM |
%r | Time, 12-hour (hh:mm:ss followed by AM or PM) |
%S | Seconds (00..59) |
%s | Seconds (00..59) |
%T | Time, 24-hour (hh:mm:ss) |
%U | Week (00..53), where Sunday is the first day of the week; WEEK() mode 0 |
%u | Week (00..53), where Monday is the first day of the week; WEEK() mode 1 |
%V | Week (01..53), where Sunday is the first day of the week; WEEK() mode 2; used with %X |
%v | Week (01..53), where Monday is the first day of the week; WEEK() mode 3; used with %x |
%W | Weekday name (Sunday..Saturday) |
%w | Day of the week (0=Sunday..6=Saturday) |
%X | Year for the week where Sunday is the first day of the week, numeric, four digits; used with %V |
%x | Year for the week, where Monday is the first day of the week, numeric, four digits; used with %v |
%Y | Year, numeric, four digits |
%y | Year, numeric (two digits) |
%% | A literal % character |
%x | x, for any “x” not listed above |
SQL query to display aggregated data when result is zero
You need to add Category to your Table2
, or (even better) have a Category
table.
Then, you need to group by those lookup tables, not the data table.
SELECT
Category.name,
Dates.Year,
Dates.Month,
SUM(Data.Value) AS Value
FROM
Category
CROSS JOIN
Dates
LEFT JOIN
Data
ON data.CategoryID = Category.ID
AND data.Year = Dates.Year
AND data.Month = Dates.Month
AND data.Property IN ('A1','B1')
AND data.Property2 IN ('C1','D1')
WHERE Dates.Year BETWEEN #### AND ####
AND Dates.Month BETWEEN #### AND ####
GROUP BY
Category.name,
Dates.Year,
Dates.Month
ORDER BY
Category.name,
Dates.Year,
Dates.Month
Display all months even if values are NULL
At first please use proper JOIN syntax and aliases.
You can create CTE with months and CTE with your output and join them:
;WITH mcte AS (
SELECT CAST('2016-01-01' as datetime) as MONTH_NAME
UNION ALL
SELECT DATEADD(MONTH,1,MONTH_NAME)
FROM mcte
WHERE DATEPART(MONTH,MONTH_NAME) < 12
), octe AS (
SELECT (DATENAME (MONTH, DATEADD ( MONTH, DATEPART(MONTH, ORDER_DATE), -1) )) AS MONTH_NAME,
SUM (o.NET_AMOUNT) AS TOTAL_SALES
FROM [ORDER] o
INNER JOIN ORDER_DETAILS od
ON o.ORDER_ID = od.ORDER_ID
--AND (DATENAME (MONTH, DATEADD ( MONTH, DATEPART(MONTH, ORDER_DATE), -1) )) = (DATENAME (MONTH, DATEADD ( MONTH, DATEPART(MONTH, @Order_month), -1) ))
GROUP BY MONTH(o.ORDER_DATE)
)
SELECT DATENAME(MONTH,m.MONTH_NAME) as MONTH_NAME,
o.TOTAL_SALES
FROM mcte m
LEFT JOIN octe o
ON o.MONTH_NAME = DATENAME(MONTH,m.MONTH_NAME)
This will gave all month names and all total sales, if there are no total sales - it will show NULLs.
Related Topics
Calculate Missing Date Ranges and Overlapping Date Ranges Between Two Dates
Does Ms Access Suppress Primary Key Violations on Inserts
Concat Group by in Vertica SQL
Why Does My Entity Framework Turn '2.87' into Just '2' (Decimal Field)
Concatenate Multiple Rows in One Field in Access
SQL to Find Duplicate Entries (Within a Group)
Storing Multiple Choice Values in Database
How to Query All Rows Within a 5-Mile Radius of My Coordinates
SQL Server Convert Datetime into Another Timezone
What's the Most Efficient Way to Normalize Text from Column into a Table
How to Distinct or Group by a Text (Or Ntext) in SQL Server 2005
How to Evaluate Expression in Select Statement in Postgres
Transposing SQLite Rows and Columns with Average Per Hour
Concat the Second Column Value If the First Column Value Is Same