How to select Overdue Rows with Date Frequencies? - mysql

+------------------------+--------+
| Invoice_id | due_date | amount |
+-------------+----------+--------+
| 20 |2020-01-18| 1250 |
+-------------+----------+--------+
| 21 |2020-01-15| 1335 |
+-------------+----------+--------+
Get all Records with date passed n days and its multiple serires
like below...
for example n=5
SELECT * FROM `invoices`
WHERE `due_date = DATE_ADD(CURDATE() + INTERVAL 5 days)
OR due_date = DATE_ADD(CURDATE() + INTERVAL 10 days)
OR due_date = DATE_ADD(CURDATE() + INTERVAL 15 days)`
but i want to make it universal for any n value

1 Way to achieve this is to generate a date range with 5 days difference and then join that with your table -
SELECT *
FROM `invoices` I
JOIN (SELECT a.Date
FROM (SELECT CURDATE() + INTERVAL (a.a + (10 * b.a) + (100 * c.a) ) * 5 DAY as Date
FROM (SELECT 0 AS a UNION ALL SELECT 1 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) AS a
CROSS JOIN (SELECT 0 AS a UNION ALL SELECT 1 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) as b
CROSS JOIN (SELECT 0 AS a UNION ALL SELECT 1 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) as c
)a
WHERE a.Date <= (SELECT MAX(due_date) FROM `invoices`)
) ON I.due_date = a.Date
I have generated only 1000 rows here. If your table is too large then you may generate 10000 rows using 1 more cross-join.

WHERE (DATEDIFF(due_date, CURDATE) % 5) = 0
It'll select all the multiple of 5 before and after today...

You can create a variable and pass it into a prepared statement like below.
SET #dateinterval = n;
SET #preparedStatement = CONCAT("
SELECT * FROM `invoices`
WHERE `due_date` IN (
(CURDATE() + INTERVAL ",#dateinterval," day),
(CURDATE() + INTERVAL ",#dateinterval*2," day),
(CURDATE() + INTERVAL ",#dateinterval*3," day)
);
");
PREPARE SQLStatement FROM #preparedStatement;
EXECUTE SQLStatement;
DEALLOCATE PREPARE SQLStatement;
Provided u pass in 5 for the #dateinterval, the above statement will resolved as:
SELECT * FROM `invoices`
WHERE `due_date` IN (
(CURDATE() + INTERVAL 5 day),
(CURDATE() + INTERVAL 10 day),
(CURDATE() + INTERVAL 15 day)
);

If you are using MySQL version 8.0 or higher, here is another alternative -
WITH CTE (RN, ID, DUE_DATE, AMT) AS(
SELECT *, ROW_NUMBER() OVER(ORDER BY due_date) RN FROM Invoices
WHERE due_date >=CURDATE()
)
SELECT * FROM CTE WHERE RN%5=0

Related

MySQL Select range date between two columns without using table columns

In My sql I want to get the date differences between two dates without using table columns
For example
SELECT BETWEEN '2012-01-10' AND '2012-01-15' as date1;
I have to get the following date as output
'2012-01-11'
'2012-01-12'
'2012-01-13'
'2012-01-14'
how to write a query to get the output as above
WITH RECURSIVE
cte AS ( SELECT #startdate + INTERVAL 1 DAY `date`
UNION ALL
SELECT `date`+ INTERVAL 1 DAY
FROM cte
WHERE `date` < #enddate - INTERVAL 1 DAY )
SELECT *
FROM cte
fiddle
MySQL 8+ needed.
my sql version used 5.6
maximum 1 year
SELECT #startdate + INTERVAL (n3.num * 7 * 7 + n2.num * 7 + n1.num + 1) DAY `date`
FROM ( SELECT 0 num union select 1 union select 2 union select 3
union select 4 union select 5 union select 6) n1
JOIN ( SELECT 0 num union select 1 union select 2 union select 3
union select 4 union select 5 union select 6) n2
JOIN ( SELECT 0 num union select 1 union select 2 union select 3
union select 4 union select 5 union select 6 union select 7) n3
HAVING `date` < #enddate;
fiddle
Max. period length is 7 * 7 * 8 = 392 days.
If you are running MySQL 8.0, you can do this with a recursive query:
with recursive cte as (
select '2012-01-10' + interval 1 day as dt
union all
select dt + interval 1 day from cte where dt + interval 1 day < '2012-01-15'
)
select * from cte
This generates one row per date in between the bounds given as input.

MySQL: How to search record for every month between two dates in mysql, return 0 if null ? with group by date clause [duplicate]

I have a table with sell orders and I want to list the COUNT of sell orders per day, between two dates, without leaving date gaps.
This is what I have currently:
SELECT COUNT(*) as Norders, DATE_FORMAT(date, "%M %e") as sdate
FROM ORDERS
WHERE date <= NOW()
AND date >= NOW() - INTERVAL 1 MONTH
GROUP BY DAY(date)
ORDER BY date ASC;
The result I'm getting is as follows:
6 May 1
14 May 4
1 May 5
8 Jun 2
5 Jun 15
But what I'd like to get is:
6 May 1
0 May 2
0 May 3
14 May 4
1 May 5
0 May 6
0 May 7
0 May 8
.....
0 Jun 1
8 Jun 2
.....
5 Jun 15
Is that possible?
Creating a range of dates on the fly and joining that against you orders table:-
SELECT sub1.sdate, COUNT(ORDERS.id) as Norders
FROM
(
SELECT DATE_FORMAT(DATE_SUB(NOW(), INTERVAL units.i + tens.i * 10 + hundreds.i * 100 DAY), "%M %e") as sdate
FROM (SELECT 0 i 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)units
CROSS JOIN (SELECT 0 i 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)tens
CROSS JOIN (SELECT 0 i 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)hundreds
WHERE DATE_SUB(NOW(), INTERVAL units.i + tens.i * 10 + hundreds.i * 100 DAY) BETWEEN DATE_SUB(NOW(), INTERVAL 1 MONTH) AND NOW()
) sub1
LEFT OUTER JOIN ORDERS
ON sub1.sdate = DATE_FORMAT(ORDERS.date, "%M %e")
GROUP BY sub1.sdate
This copes with date ranges of up to 1000 days.
Note that it could be made more efficient easily depending on the type of field you are using for your dates.
EDIT - as requested, to get the count of orders per month:-
SELECT aMonth, COUNT(ORDERS.id) as Norders
FROM
(
SELECT DATE_FORMAT(DATE_SUB(NOW(), INTERVAL months.i MONTH), "%Y%m") as sdate, DATE_FORMAT(DATE_SUB(NOW(), INTERVAL months.i MONTH), "%M") as aMonth
FROM (SELECT 0 i 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 UNION SELECT 10 UNION SELECT 11)months
WHERE DATE_SUB(NOW(), INTERVAL months.i MONTH) BETWEEN DATE_SUB(NOW(), INTERVAL 12 MONTH) AND NOW()
) sub1
LEFT OUTER JOIN ORDERS
ON sub1.sdate = DATE_FORMAT(ORDERS.date, "%Y%m")
GROUP BY aMonth
You are going to need to generate a virtual (or physical) table, containing every date in the range.
That can be done as follows, using a sequence table.
SELECT mintime + INTERVAL seq.seq DAY AS orderdate
FROM (
SELECT CURDATE() - INTERVAL 1 MONTH AS mintime,
CURDATE() AS maxtime
FROM obs
) AS minmax
JOIN seq_0_to_999999 AS seq ON seq.seq < TIMESTAMPDIFF(DAY,mintime,maxtime)
Then, you join this virtual table to your query, as follows.
SELECT IFNULL(orders.Norders,0) AS Norders, /* show zero instead of null*/
DATE_FORMAT(alldates.orderdate, "%M %e") as sdate
FROM (
SELECT mintime + INTERVAL seq.seq DAY AS orderdate
FROM (
SELECT CURDATE() - INTERVAL 1 MONTH AS mintime,
CURDATE() AS maxtime
FROM obs
) AS minmax
JOIN seq_0_to_999999 AS seq
ON seq.seq < TIMESTAMPDIFF(DAY,mintime,maxtime)
) AS alldates
LEFT JOIN (
SELECT COUNT(*) as Norders, DATE(date) AS orderdate
FROM ORDERS
WHERE date <= NOW()
AND date >= NOW() - INTERVAL 1 MONTH
GROUP BY DAY(date)
) AS orders ON alldates.orderdate = orders.orderdate
ORDER BY alldates.orderdate ASC
Notice that you need the LEFT JOIN so the rows in your output result set will be preserved even if there's no data in your ORDERS table.
Where do you get this sequence table seq_0_to_999999? You can make it like this.
DROP TABLE IF EXISTS seq_0_to_9;
CREATE TABLE seq_0_to_9 AS
SELECT 0 AS seq 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;
DROP VIEW IF EXISTS seq_0_to_999;
CREATE VIEW seq_0_to_999 AS (
SELECT (a.seq + 10 * (b.seq + 10 * c.seq)) AS seq
FROM seq_0_to_9 a
JOIN seq_0_to_9 b
JOIN seq_0_to_9 c
);
DROP VIEW IF EXISTS seq_0_to_999999;
CREATE VIEW seq_0_to_999999 AS (
SELECT (a.seq + (1000 * b.seq)) AS seq
FROM seq_0_to_999 a
JOIN seq_0_to_999 b
);
You can find an explanation of all this in more detail at http://www.plumislandmedia.net/mysql/filling-missing-data-sequences-cardinal-integers/
If you're using MariaDB version 10+, these sequence tables are built in.
First create a Calendar Table
SELECT coalesce(COUNT(O.*),0) as Norders, DATE_FORMAT(C.date, "%M %e") as sdate
FROM Calendar C
LEFT JOIN ORDERS O ON C.date=O.date
WHERE O.date <= NOW() AND O.date >= NOW() - INTERVAL 1 MONTH
GROUP BY DAY(date)
ORDER BY date ASC;

SQL query - counting what is not there [duplicate]

I have a database of users. I would like to create a graph based on userbase growth. The query I have now is:
SELECT DATE(datecreated), count(*) AS number FROM users
WHERE DATE(datecreated) > '2009-06-21' AND DATE(datecreated) <= DATE(NOW())
GROUP BY DATE(datecreated) ORDER BY datecreated ASC
This returns almost what I want. If we get 0 users one day, that day is not returned as a 0 value, it is just skipped and the next day that has at least one user is returned. How can I get something like (psuedo-response):
date1 5
date2 8
date3 0
date4 0
date5 9
etc...
where the dates with zero show up in sequential order with the rest of the dates?
Thanks!
I hope you will figure out the rest.
select * from (
select date_add('2003-01-01 00:00:00.000', INTERVAL n5.num*10000+n4.num*1000+n3.num*100+n2.num*10+n1.num DAY ) as date from
(select 0 as num
union all select 1
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) n1,
(select 0 as num
union all select 1
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) n2,
(select 0 as num
union all select 1
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) n3,
(select 0 as num
union all select 1
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) n4,
(select 0 as num
union all select 1
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) n5
) a
where date >'2011-01-02 00:00:00.000' and date < NOW()
order by date
With
select n3.num*100+n2.num*10+n1.num as date
you will get a column with numbers from 0 to max(n3)*100+max(n2)*10+max(n1)
Since here we have max n3 as 3, SELECT will return 399, plus 0 -> 400 records (dates in calendar).
You can tune your dynamic calendar by limiting it, for example, from min(date) you have to now().
This question asks the same thing I think. Generally the accepted answer seems to be that you either do it in your application logic (read in what you have into an array, then loop through the array and create the missing dates), or you use temporary tables filled with the dates you wish to join.
This is better to do as:
-- 7 Days:
set #n:=date(now() + interval 1 day);
SELECT qb.day_series as days , COALESCE(col_byte, 0) as Bytes from tbl1 qa
right join (
select (select #n:= #n - interval 1 day) day_series from tbl1 limit 7 ) as qb
on date(qa.Timestamp) = qb.day_series and
qa.Timestamp > DATE_SUB(curdate(), INTERVAL 7 day) order by qb.day_series asc
-- 30 Days:
set #n:=date(now() + interval 1 day);
SELECT qb.day_series as days , COALESCE(col_byte, 0) as Bytes from tbl1 qa
right join (
select (select #n:= #n - interval 1 day) day_series from tbl1 limit 30 ) as qb
on date(qa.Timestamp) = qb.day_series and
qa.Timestamp > DATE_SUB(curdate(), INTERVAL 30 day) order by qb.day_series asc;
or without variable like this:
SELECT qb.day_series as days , COALESCE(col_byte, 0) as Bytes from tbl1 qa
right join (
select curdate() - INTERVAL a.a day as day_series from(
select 0 as a union all select 1 union all select 2 union all
select 3 union all select 4 union all
select 5 union all select 6 union all select 7
) as a ) as qb
on date(qa.Timestamp) = qb.day_series and
qa.Timestamp > DATE_SUB(curdate(), INTERVAL 7 day) order by qb.day_series asc;
Do a right outer join to a table, call it tblCalendar, that is pre-populated with the dates you wish to report on. And join on the date field.
Paul
Query is:
SELECT qb.dy as yourday, COALESCE(count(yourcolumn), 0) as yourcount from yourtable qa
right join (
select curdate() as dy union
select DATE_SUB(curdate(), INTERVAL 1 day) as dy union
select DATE_SUB(curdate(), INTERVAL 2 day) as dy union
select DATE_SUB(curdate(), INTERVAL 3 day) as dy union
select DATE_SUB(curdate(), INTERVAL 4 day) as dy union
select DATE_SUB(curdate(), INTERVAL 5 day) as dy union
select DATE_SUB(curdate(), INTERVAL 6 day) as dy
) as qb
on qa.dates = qb.dy
and qa.dates > DATE_SUB(curdate(), INTERVAL 7 day)
order by qb.dy asc;
and the result is:
+------------+-----------+
| yourday | yourcount |
+------------+-----------+
| 2015-06-24 | 274339 |
| 2015-06-25 | 0 |
| 2015-06-26 | 0 |
| 2015-06-27 | 0 |
| 2015-06-28 | 134703 |
| 2015-06-29 | 87613 |
| 2015-06-30 | 0 |
+------------+-----------+
On further thought, something like this should be what you want:
CREATE TEMPORARY TABLE DateSummary1 ( datenew timestamp ) SELECT DISTINCT(DATE(datecreated)) as datenew FROM users;
CREATE TEMPORARY TABLE DateSummary2 ( datenew timestamp, number int ) SELECT DATE(datecreated) as datenew, count(*) AS number FROM users
WHERE DATE(datecreated) > '2009-06-21' AND DATE(datecreated) <= DATE(NOW())
GROUP BY DATE(datecreated) ORDER BY datecreated ASC;
SELECT ds1.datenew,ds2.number FROM DateSummary1 ds1 LEFT JOIN DateSummary2 ds2 on ds1.datenew=ds2.datenew;
This gives you all the dates in the first table, and the count summary data in the second table. You might need to replace ds2.number with IF(ISNULL(ds2.number),0,ds2.number) or something similar.

Aggregating data by date in a date range without date gaps in result set

I have a table with sell orders and I want to list the COUNT of sell orders per day, between two dates, without leaving date gaps.
This is what I have currently:
SELECT COUNT(*) as Norders, DATE_FORMAT(date, "%M %e") as sdate
FROM ORDERS
WHERE date <= NOW()
AND date >= NOW() - INTERVAL 1 MONTH
GROUP BY DAY(date)
ORDER BY date ASC;
The result I'm getting is as follows:
6 May 1
14 May 4
1 May 5
8 Jun 2
5 Jun 15
But what I'd like to get is:
6 May 1
0 May 2
0 May 3
14 May 4
1 May 5
0 May 6
0 May 7
0 May 8
.....
0 Jun 1
8 Jun 2
.....
5 Jun 15
Is that possible?
Creating a range of dates on the fly and joining that against you orders table:-
SELECT sub1.sdate, COUNT(ORDERS.id) as Norders
FROM
(
SELECT DATE_FORMAT(DATE_SUB(NOW(), INTERVAL units.i + tens.i * 10 + hundreds.i * 100 DAY), "%M %e") as sdate
FROM (SELECT 0 i 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)units
CROSS JOIN (SELECT 0 i 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)tens
CROSS JOIN (SELECT 0 i 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)hundreds
WHERE DATE_SUB(NOW(), INTERVAL units.i + tens.i * 10 + hundreds.i * 100 DAY) BETWEEN DATE_SUB(NOW(), INTERVAL 1 MONTH) AND NOW()
) sub1
LEFT OUTER JOIN ORDERS
ON sub1.sdate = DATE_FORMAT(ORDERS.date, "%M %e")
GROUP BY sub1.sdate
This copes with date ranges of up to 1000 days.
Note that it could be made more efficient easily depending on the type of field you are using for your dates.
EDIT - as requested, to get the count of orders per month:-
SELECT aMonth, COUNT(ORDERS.id) as Norders
FROM
(
SELECT DATE_FORMAT(DATE_SUB(NOW(), INTERVAL months.i MONTH), "%Y%m") as sdate, DATE_FORMAT(DATE_SUB(NOW(), INTERVAL months.i MONTH), "%M") as aMonth
FROM (SELECT 0 i 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 UNION SELECT 10 UNION SELECT 11)months
WHERE DATE_SUB(NOW(), INTERVAL months.i MONTH) BETWEEN DATE_SUB(NOW(), INTERVAL 12 MONTH) AND NOW()
) sub1
LEFT OUTER JOIN ORDERS
ON sub1.sdate = DATE_FORMAT(ORDERS.date, "%Y%m")
GROUP BY aMonth
You are going to need to generate a virtual (or physical) table, containing every date in the range.
That can be done as follows, using a sequence table.
SELECT mintime + INTERVAL seq.seq DAY AS orderdate
FROM (
SELECT CURDATE() - INTERVAL 1 MONTH AS mintime,
CURDATE() AS maxtime
FROM obs
) AS minmax
JOIN seq_0_to_999999 AS seq ON seq.seq < TIMESTAMPDIFF(DAY,mintime,maxtime)
Then, you join this virtual table to your query, as follows.
SELECT IFNULL(orders.Norders,0) AS Norders, /* show zero instead of null*/
DATE_FORMAT(alldates.orderdate, "%M %e") as sdate
FROM (
SELECT mintime + INTERVAL seq.seq DAY AS orderdate
FROM (
SELECT CURDATE() - INTERVAL 1 MONTH AS mintime,
CURDATE() AS maxtime
FROM obs
) AS minmax
JOIN seq_0_to_999999 AS seq
ON seq.seq < TIMESTAMPDIFF(DAY,mintime,maxtime)
) AS alldates
LEFT JOIN (
SELECT COUNT(*) as Norders, DATE(date) AS orderdate
FROM ORDERS
WHERE date <= NOW()
AND date >= NOW() - INTERVAL 1 MONTH
GROUP BY DAY(date)
) AS orders ON alldates.orderdate = orders.orderdate
ORDER BY alldates.orderdate ASC
Notice that you need the LEFT JOIN so the rows in your output result set will be preserved even if there's no data in your ORDERS table.
Where do you get this sequence table seq_0_to_999999? You can make it like this.
DROP TABLE IF EXISTS seq_0_to_9;
CREATE TABLE seq_0_to_9 AS
SELECT 0 AS seq 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;
DROP VIEW IF EXISTS seq_0_to_999;
CREATE VIEW seq_0_to_999 AS (
SELECT (a.seq + 10 * (b.seq + 10 * c.seq)) AS seq
FROM seq_0_to_9 a
JOIN seq_0_to_9 b
JOIN seq_0_to_9 c
);
DROP VIEW IF EXISTS seq_0_to_999999;
CREATE VIEW seq_0_to_999999 AS (
SELECT (a.seq + (1000 * b.seq)) AS seq
FROM seq_0_to_999 a
JOIN seq_0_to_999 b
);
You can find an explanation of all this in more detail at http://www.plumislandmedia.net/mysql/filling-missing-data-sequences-cardinal-integers/
If you're using MariaDB version 10+, these sequence tables are built in.
First create a Calendar Table
SELECT coalesce(COUNT(O.*),0) as Norders, DATE_FORMAT(C.date, "%M %e") as sdate
FROM Calendar C
LEFT JOIN ORDERS O ON C.date=O.date
WHERE O.date <= NOW() AND O.date >= NOW() - INTERVAL 1 MONTH
GROUP BY DAY(date)
ORDER BY date ASC;

MySQL: Select All Dates In a Range Even If No Records Present

I have a database of users. I would like to create a graph based on userbase growth. The query I have now is:
SELECT DATE(datecreated), count(*) AS number FROM users
WHERE DATE(datecreated) > '2009-06-21' AND DATE(datecreated) <= DATE(NOW())
GROUP BY DATE(datecreated) ORDER BY datecreated ASC
This returns almost what I want. If we get 0 users one day, that day is not returned as a 0 value, it is just skipped and the next day that has at least one user is returned. How can I get something like (psuedo-response):
date1 5
date2 8
date3 0
date4 0
date5 9
etc...
where the dates with zero show up in sequential order with the rest of the dates?
Thanks!
I hope you will figure out the rest.
select * from (
select date_add('2003-01-01 00:00:00.000', INTERVAL n5.num*10000+n4.num*1000+n3.num*100+n2.num*10+n1.num DAY ) as date from
(select 0 as num
union all select 1
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) n1,
(select 0 as num
union all select 1
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) n2,
(select 0 as num
union all select 1
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) n3,
(select 0 as num
union all select 1
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) n4,
(select 0 as num
union all select 1
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) n5
) a
where date >'2011-01-02 00:00:00.000' and date < NOW()
order by date
With
select n3.num*100+n2.num*10+n1.num as date
you will get a column with numbers from 0 to max(n3)*100+max(n2)*10+max(n1)
Since here we have max n3 as 3, SELECT will return 399, plus 0 -> 400 records (dates in calendar).
You can tune your dynamic calendar by limiting it, for example, from min(date) you have to now().
This question asks the same thing I think. Generally the accepted answer seems to be that you either do it in your application logic (read in what you have into an array, then loop through the array and create the missing dates), or you use temporary tables filled with the dates you wish to join.
This is better to do as:
-- 7 Days:
set #n:=date(now() + interval 1 day);
SELECT qb.day_series as days , COALESCE(col_byte, 0) as Bytes from tbl1 qa
right join (
select (select #n:= #n - interval 1 day) day_series from tbl1 limit 7 ) as qb
on date(qa.Timestamp) = qb.day_series and
qa.Timestamp > DATE_SUB(curdate(), INTERVAL 7 day) order by qb.day_series asc
-- 30 Days:
set #n:=date(now() + interval 1 day);
SELECT qb.day_series as days , COALESCE(col_byte, 0) as Bytes from tbl1 qa
right join (
select (select #n:= #n - interval 1 day) day_series from tbl1 limit 30 ) as qb
on date(qa.Timestamp) = qb.day_series and
qa.Timestamp > DATE_SUB(curdate(), INTERVAL 30 day) order by qb.day_series asc;
or without variable like this:
SELECT qb.day_series as days , COALESCE(col_byte, 0) as Bytes from tbl1 qa
right join (
select curdate() - INTERVAL a.a day as day_series from(
select 0 as a union all select 1 union all select 2 union all
select 3 union all select 4 union all
select 5 union all select 6 union all select 7
) as a ) as qb
on date(qa.Timestamp) = qb.day_series and
qa.Timestamp > DATE_SUB(curdate(), INTERVAL 7 day) order by qb.day_series asc;
Do a right outer join to a table, call it tblCalendar, that is pre-populated with the dates you wish to report on. And join on the date field.
Paul
Query is:
SELECT qb.dy as yourday, COALESCE(count(yourcolumn), 0) as yourcount from yourtable qa
right join (
select curdate() as dy union
select DATE_SUB(curdate(), INTERVAL 1 day) as dy union
select DATE_SUB(curdate(), INTERVAL 2 day) as dy union
select DATE_SUB(curdate(), INTERVAL 3 day) as dy union
select DATE_SUB(curdate(), INTERVAL 4 day) as dy union
select DATE_SUB(curdate(), INTERVAL 5 day) as dy union
select DATE_SUB(curdate(), INTERVAL 6 day) as dy
) as qb
on qa.dates = qb.dy
and qa.dates > DATE_SUB(curdate(), INTERVAL 7 day)
order by qb.dy asc;
and the result is:
+------------+-----------+
| yourday | yourcount |
+------------+-----------+
| 2015-06-24 | 274339 |
| 2015-06-25 | 0 |
| 2015-06-26 | 0 |
| 2015-06-27 | 0 |
| 2015-06-28 | 134703 |
| 2015-06-29 | 87613 |
| 2015-06-30 | 0 |
+------------+-----------+
On further thought, something like this should be what you want:
CREATE TEMPORARY TABLE DateSummary1 ( datenew timestamp ) SELECT DISTINCT(DATE(datecreated)) as datenew FROM users;
CREATE TEMPORARY TABLE DateSummary2 ( datenew timestamp, number int ) SELECT DATE(datecreated) as datenew, count(*) AS number FROM users
WHERE DATE(datecreated) > '2009-06-21' AND DATE(datecreated) <= DATE(NOW())
GROUP BY DATE(datecreated) ORDER BY datecreated ASC;
SELECT ds1.datenew,ds2.number FROM DateSummary1 ds1 LEFT JOIN DateSummary2 ds2 on ds1.datenew=ds2.datenew;
This gives you all the dates in the first table, and the count summary data in the second table. You might need to replace ds2.number with IF(ISNULL(ds2.number),0,ds2.number) or something similar.