30

What is the best way in mysql to generate a series of dates in a given range?

The application I have in mind is to write a report query that returns a row for every date, regardless of whether there is any data to report. In its simplest form:

select dates.date, sum(sales.amount)
from <series of dates between X and Y> dates
left join sales on date(sales.created) = dates.date
group by 1

I have tried creating a table with lots of dates, but that seems like a poor workaround.

Bohemian
  • 412,405
  • 93
  • 575
  • 722

4 Answers4

40

if you're in a situation like me where creating temporary tables is prohibited, and setting variables is also not allowed, but you want to generate a list of dates in a specific period, say current year to do some aggregation, use this

select * from 
(select adddate('1970-01-01',t4*10000 + t3*1000 + t2*100 + t1*10 + t0) gen_date from
 (select 0 t0 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
 (select 0 t1 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
 (select 0 t2 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
 (select 0 t3 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
 (select 0 t4 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t4) v
where gen_date between '2017-01-01' and '2017-12-31'
computingfreak
  • 4,939
  • 1
  • 34
  • 51
24

I think having a calendar table is a good idea; you can gain a lot of reporting and query functionality, especially when filling sparse data ranges.

I found this article with what seems to be a good example.

BellevueBob
  • 9,498
  • 5
  • 29
  • 56
  • This is a great idea. Thanks for the article link. – Alagu Feb 24 '20 at 16:34
  • @BellevueBob The website is down with the error: PDOException: SQLSTATE[HY000] [2003] Can't connect to MySQL server on '127.0.0.1' (111) in lock_may_be_available() (line 167 of /var/www/www.brianshowalter.com/includes/lock.inc). – ptyshevs Apr 27 '21 at 06:30
8

You may use a variable generate date series:

Set @i:=0;
SELECT DATE(DATE_ADD(X, 
INTERVAL @i:=@i+1 DAY) ) AS datesSeries
FROM yourtable, (SELECT @i:=0) r
where @i < DATEDIFF(now(), date Y) 
;

Not sure if this is what you have tried :) though.

Next use above generated query as a table to left join:

set @i:=0;

select
d.dates,
sum(s.amount) as TotalAmount
from(
SELECT DATE(DATE_ADD(X, 
INTERVAL @i:=@i+1 DAY) ) AS dateSeries
FROM Sales, (SELECT @i:=0) r
where @i < DATEDIFF(now(), date Y) 
) dates d 
left join Sales s
on Date(s.Created) = Date(d.dateSeries)
group by 1
;
bonCodigo
  • 14,268
  • 1
  • 48
  • 91
5

You can also use Temporary Table to generate date series. Check below query:

CREATE TEMPORARY TABLE daterange (dte DATE); 

SET @counter := -1;
WHILE (@counter < DATEDIFF(DATE(_todate), DATE(_fromdate))) DO 
    INSERT daterange VALUES (DATE_ADD(_fromdate, INTERVAL @counter:=@counter + 1 DAY));
END WHILE;

SELECT dates.dte, SUM(sales.amount)
FROM daterange dates
LEFT JOIN sales ON DATE(sales.created) = dates.date
GROUP BY dates.dte;
Saharsh Shah
  • 28,687
  • 8
  • 48
  • 83