selecting between date range and forcing empty rows to be 0?
-
28-10-2019 - |
Question
I have a table that looks like
expires | value
-------------------
2011-06-15 | 15
2011-06-15 | 15
2011-06-25 | 15
2011-07-15 | 15
2011-07-15 | 15
2011-07-25 | 15
2011-08-15 | 15
2011-08-15 | 15
2011-08-25 | 15
I want to run a query that will spit out
June | 45
July | 45
August | 45
So my query is
SELECT SUM(amount) AS `amount`,
DATE_FORMAT(expires , '%M') AS `month`
FROM dealDollars
WHERE DATE(expires) BETWEEN DATE(NOW())
AND LAST_DAY(DATE(NOW()+INTERVAL 3 MONTH))
GROUP BY MONTH(expires)
Which works fine. But with the result, if there were no rows in say July, July would not show up.
How can I force July to show up with 0 as its value?
Solution
There is no easy way to do this. One possible way is to have a table called months: Which will have 12 rows: (January, February, ..., December)
You can left join the Months table with the query you have to get the desired output.
OTHER TIPS
The general consensus is that you should just create a table of month names. What follows is a silly solution which can serve as a workaround.
You'll have to work on the specifics yourself, but have you looked at sub-queries in the from clause?
Basically, it would be something like this:
SELECT NVL(B.amount, 0) as `amount`, A.month as `month`
FROM (SELECT 'January' as `month`
UNION SELECT 'February' as `month`
UNION SELECT 'March' as `month`...
UNION SELECT 'DECEMBER' as `month`) as A
LEFT JOIN
(SELECT SUM(amount) AS `amount`,
DATE_FORMAT(expires , '%M') AS `month`
FROM dealDollars
WHERE
DATE(expires) BETWEEN
DATE(NOW()) AND
LAST_DAY(DATE(NOW()+INTERVAL 3 MONTH))
GROUP BY MONTH(expires)) as B
ON (A.MONTH = B.MONTH)
Crazy, no?
MySQL doesn't have recursive functionality, so you're left with using the NUMBERS
table trick -
Create a table that only holds incrementing numbers - easy to do using an auto_increment:
DROP TABLE IF EXISTS `example`.`numbers`; CREATE TABLE `example`.`numbers` ( `id` int(10) unsigned NOT NULL auto_increment, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Populate the table using:
INSERT INTO NUMBERS (id) VALUES (NULL)
...for as many values as you need. In this case, the INSERT statement needs to be run at least 3 times.
Use DATE_ADD to construct a list of days, increasing based on the
NUMBERS.id
value:SELECT x.dt FROM (SELECT DATE(DATE_SUB(CURRENT_DATE(), INTERVAL (n.id - 1) MONTH)) AS dt FROM numbers n WHERE DATE(DATE_SUB(CURRENT_DATE(), INTERVAL (n.id - 1) MONTH)) BETWEEN CURRENT_DATE() AND LAST_DAY(CURRENT_DATE() +INTERVAL 3 MONTH)) ) x
Use an OUTER JOIN to get your desired output:
SELECT x.dt, COUNT(*) AS total FROM (SELECT DATE(DATE_SUB(CURRENT_DATE(), INTERVAL (n.id - 1) MONTH)) AS dt FROM numbers n WHERE DATE(DATE_SUB(CURRENT_DATE(), INTERVAL (n.id - 1) MONTH)) BETWEEN CURRENT_DATE() AND LAST_DAY(CURRENT_DATE() +INTERVAL 3 MONTH)) ) x LEFT JOIN YOUR_TABLE y ON y.date = x.dt GROUP BY x.dt ORDER BY x.dt
Why Numbers, not Dates?
Simple - dates can be generated based on the number, like in the example I provided. It also means using a single table, vs say one per data type.
select MONTHNAME(expires) as month_name,sum(`value`) from Table1
group by month_name order by null;