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?

Was it helpful?

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 -

  1. 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;
    
  2. 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.

  3. 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
    
  4. 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;

fiddle

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top