Hour Wise data in mySql - mysql

I have the table with following fields
Createdon(datetime)
Amount(double)
I need to find the sum of amounts for next 24 hours of the given date. If there are no results then the sum should be zero.
e.g
duration sum
0000-0001 25.43
0001-0002 36.85
0002-0003 0
.
.
.
.
0022-0023 38.56
Can you please help me creating a query to find the required solution

The key to your query is the ability to take any datetime value and truncate it to the nearest preceding hour. You can do that with this expression:
DATE_FORMAT(Createdon, '%Y-%m-%d %H:00')
Given, for example, 2015-04-21 14:22:05, this gives back 2015-04-21 14:00:00.
Then you use that in GROUP BY
SELECT DATE_FORMAT(Createdon, '%Y-%m-%d %H:00') Createdhour,
SUM(Amount) sum
FROM theTable
GROUP BY DATE_FORMAT(Createdon, '%Y-%m-%d %H:00')
ORDER BY DATE_FORMAT(Createdon, '%Y-%m-%d %H:00')
Finally, I think you wanted one day's worth of results. You need to add a WHERE clause to get that. The one shown here will take yesterday's results -- that is, all results from [midnight yesterday -- midnight today).
SELECT DATE_FORMAT(Createdon, '%Y-%m-%d %H:00') Createdhour,
SUM(Amount) sum
FROM theTable
WHERE CreatedOn >= DATE(NOW()) - INTERVAL 1 DAY
AND CreatedOn < DATE(NOW())
GROUP BY DATE_FORMAT(Createdon, '%Y-%m-%d %H:00')
ORDER BY DATE_FORMAT(Createdon, '%Y-%m-%d %H:00')
This is explained in greater detail at http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/
To include all hours of the day, you will need an independent source of distinct DATETIME items.
Here's a query that will do such a thing.
SELECT mintime + INTERVAL seq.seq HOUR AS CreatedHour
FROM (
SELECT DATE(NOW()) - INTERVAL 1 DAY AS mintime,
DATE(NOW()) AS maxtime
) AS minmax
JOIN seq_0_to_23 AS seq
ON seq.seq < TIMESTAMPDIFF(HOUR,mintime,maxtime)
You then need to use LEFT JOIN to pick up your data.
SELECT a.Createdhour,
SUM(Amount) sum
FROM (
SELECT mintime + INTERVAL seq.seq HOUR AS CreatedHour
FROM (
SELECT DATE(NOW()) - INTERVAL 1 DAY AS mintime,
DATE(NOW()) AS maxtime
) AS minmax
JOIN seq_0_to_23 AS seq
ON seq.seq < TIMESTAMPDIFF(HOUR,mintime,maxtime)
) a
LEFT JOIN theTable t
ON a.CreatedHour = DATE_FORMAT(t.Createdon, '%Y-%m-%d %H:00')
GROUP BY DATE_FORMAT(t.Createdon, '%Y-%m-%d %H:00')
ORDER BY DATE_FORMAT(t.Createdon, '%Y-%m-%d %H:00')
Finally, you need to somehow get that table seq_0_to_23. If you're running MariaDB, it's built in. If not...
CREATE TABLE seq_0_to_23 AS
SELECT 0 AS seq
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
UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15
UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18
UNION ALL SELECT 19 UNION ALL SELECT 20 UNION ALL SELECT 21
UNION ALL SELECT 22 UNION ALL SELECT 23
This is written up in more general form at http://www.plumislandmedia.net/mysql/filling-missing-data-sequences-cardinal-integers/

Related

Neat Way to select every month in SQL

I am trying to find number of users every month.
This is my SQL which I learn from another question.
The part for creating number of month is easy to understand but it is long. I am wondering is there a neater way to write the same SQL. Thanks.
SELECT
meses.MONTH,
COUNT(Users.user_ID) AS num_of_user
FROM
(
SELECT
1 AS MONTH
UNION
SELECT
2 AS MONTH
UNION
SELECT
3 AS MONTH
UNION
SELECT
4 AS MONTH
UNION
SELECT
5 AS MONTH
UNION
SELECT
6 AS MONTH
UNION
SELECT
7 AS MONTH
UNION
SELECT
8 AS MONTH
UNION
SELECT
9 AS MONTH
UNION
SELECT
10 AS MONTH
UNION
SELECT
11 AS MONTH
UNION
SELECT
12 AS MONTH
) AS meses
LEFT JOIN
Users
ON
meses.month = MONTH(Users.joint_date) AND YEAR(Users.joint_date) = '2000'
GROUP BY
meses.MONTN
In MySQL 8.0, you can use a recursive query to generate the series.
I would also recommend filtering against literal dates rather than applying date function on the column being filtered: this is much more efficient, and can take advantage of an index on users(joint_date).
with dates as (
select '2020-01-01' dt
union all select dt + interval 1 month from dates where dt + interval 1 month < '2021-01-01'
)
select d.dt, count(u.user_id) as num_of_users
from dates d
left join users u
on u.joint_date >= d.dt
and u.joint_date < d.dt + interval 1 month
group by d.dt
In earlier versions, you do need to enumerate the dates, using union. However I would still recommend the literal date technique. That would look like:
select '2020-01-01' + interval n.n month as dt, count(u.user_id) as num_of_users
from (select 0 n union all select 2 ... union all select 11) n
left join users u
on u.joint_date >= '2020-01-01' + interval n.n month
and u.joint_date < '2020-01-01' + interval (n.n + 1) month
group by n.n

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;

Elapsed Time Between Two Dates for specified time range

I have a MYSQL table with a TIMESTAMP column 'Start' and a TIMESTAMP column 'End'. I want to return the number of minutes between the start and the end (End is always after than Start). Usually I'd just use 'TIMESTAMPDIFF()' but this time I need to get the minutes from 9am until 22pm, of each day in that date range.
If a row has a Start '2017-01-01 07:15:00' and an End of '2017-01-02 11:30:00' - the elapsed time should be 15.5 hours (930 minutes).
I'm having trouble coming up with a decent way of doing this and my searching online hasn't found quite what I'm looking for. Can someone help me along?
Edit:
CREATE TABLE date_ranges (
Start TIMESTAMP,
End TIMESTAMP
);
INSERT INTO date_ranges VALUES('2017-01-01 07:15:00','2017-01-02 11:30:00');
I came up with this:
SELECT Start, End, TIMESTAMPDIFF(MINUTE, Start, End) AS MinutesElapsed
FROM date_ranges;
I'm missing the part where the time in minutes is calculated only in the specified time range (9am until 22pm). Any ideas?
Here you go:
SELECT t1, t2, (TIMESTAMPDIFF(MINUTE, t1, t2) - TIMESTAMPDIFF(DAY, t1, t2)*660) FROM
(SELECT CASE WHEN t1 < STR_TO_DATE(concat(date_format(t1, '%Y-%m-%d'), ' 09:00:00'), '%Y-%m-%d %h:%i:%s')
THEN STR_TO_DATE(concat(date_format(t1, '%Y-%m-%d'), ' 09:00:00'), '%Y-%m-%d %h:%i:%s')
ELSE t1
END AS t1 FROM test) test1,
(SELECT CASE WHEN t2 > STR_TO_DATE(concat(date_format(t2, '%Y-%m-%d'), ' 22:00:00'), '%Y-%m-%d %h:%i:%s')
THEN STR_TO_DATE(concat(date_format(t2, '%Y-%m-%d'), ' 22:00:00'), '%Y-%m-%d %h:%i:%s')
ELSE t2
END AS t2 FROM test) test2;
660 = number of minutes between 22:00 and 09:00 (11 hours)
Here's the SQL Fiddle.
It's not very concise, but this should give you the results you want:
select started_at,ended_at,
(case
when date(ended_at) = date(started_at)
then
timestampdiff(
minute,
greatest(started_at,concat(date(started_at),' 09:00:00')),
least(ended_at,concat(date(ended_at),' 22:00:00'))
)
else
timestampdiff(
minute,
least(greatest(started_at,concat(date(started_at),' 09:00:00')),concat(date(started_at),' 22:00:00')),
concat(date(started_at),' 22:00:00')
)
+
timestampdiff(
minute,
concat(date(ended_at),' 09:00:00'),
greatest(least(ended_at,concat(date(ended_at),' 22:00:00')),concat(date(ended_at),' 09:00:00'))
)
+ ((datediff(ended_at,started_at)-1)*780)
end) as total_minutes
from your_table;
--Generating all dates in 2017.
CREATE TABLE CALENDAR AS --Use a different table name if CALENDAR already exists
SELECT '2017-12-31 09:00:00' - INTERVAL c.number DAY AS start_datetime,'2017-12-31 22:00:00' - INTERVAL c.number DAY AS end_datetime
FROM (SELECT singles + tens + hundreds number FROM
(SELECT 0 singles
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
) singles JOIN
(SELECT 0 tens
UNION ALL SELECT 10 UNION ALL SELECT 20 UNION ALL SELECT 30
UNION ALL SELECT 40 UNION ALL SELECT 50 UNION ALL SELECT 60
UNION ALL SELECT 70 UNION ALL SELECT 80 UNION ALL SELECT 90
) tens JOIN
(SELECT 0 hundreds
UNION ALL SELECT 100 UNION ALL SELECT 200 UNION ALL SELECT 300
UNION ALL SELECT 400 UNION ALL SELECT 500 UNION ALL SELECT 600
UNION ALL SELECT 700 UNION ALL SELECT 800 UNION ALL SELECT 900
) hundreds
ORDER BY number DESC) c
WHERE c.number BETWEEN 0 and 364
;
--End of table creation
--Actual query begins here
SELECT D.`START`,
D.`END`,
SUM(TIMESTAMPDIFF(MINUTE,GREATEST(D.`START`,C.START_DATETIME), LEAST(D.`END`,C.END_DATETIME))) AS TOTAL_TIME
FROM CALENDAR C
LEFT JOIN DATE_RANGES D ON DATE(C.START_DATETIME) >= DATE(D.`START`)
AND DATE(C.START_DATETIME) <= DATE(D.`END`)
WHERE D.`START` IS NOT NULL
AND D.`END` IS NOT NULL
GROUP BY D.`START`,
D.`END`
;
Construct a calendar table with a dates for a specified number of years. Each date having a start time of 09:00 and an end time of 22:00.
Left join on this table to get one row per date from the date ranges table.
Sum up the differences each day to get the total time worked.
Sample Demo
Day 1 Day 2 Day 3
|--********--|--********--|--********--|
|__________________________|
The question, IMHO is to know how many minutes the first day, and how many minutes the last day, the intermediate days have 780 minutes.
I've used a subquery just to help in the intermediate calculations.
select
if(hour(t1) < 9, date(t1) + interval 9 hour , t1) as tIni1,
date(t1) + interval 22 hour as tFin1,
date(t2) + interval 9 hour as tIni2,
if(hour(t2) > 22, date(t2) + interval 22 hour, t2) as tFin2,
TIMESTAMPDIFF(day, date(t1), date(t2)) numDays
from
tdt
tIni1 and tFin1 is the period of the first day, and tIni2, tFin2 the period of the last day, obviously first and last day can be the same.
Then calculate minutes of first day + minutes of second day + 780 minutes for every intermediate day.
select numDays, tIni1, tFin1, tIni2, tFin2,
if (numDays = 0,
TIMESTAMPDIFF(minute, tIni1, tFin2),
TIMESTAMPDIFF(minute, tIni1, tFin1)
+ TIMESTAMPDIFF(minute, tIni2, tFin2)
+ (numDays - 1) * 780
) as Minutes
from (
select
if(hour(t1) < 9, date(t1) + interval 9 hour , t1) as tIni1,
date(t1) + interval 22 hour as tFin1,
date(t2) + interval 9 hour as tIni2,
if(hour(t2) > 22, date(t2) + interval 22 hour, t2) as tFin2,
TIMESTAMPDIFF(day, date(t1), date(t2)) numDays
from
tdt
) ti
;
Try it here: http://rextester.com/GDHAB78973

MySQL - Select number of entries the last 7 days

This is how my table looks like
-----------------------
posts
----------------------
id
created_at
..
..
How should the MySQL Query look like, so that i get the number of entries the last 7 days.
The result should look something like that:
['Mon' => 234, 'Tues' => 12, ...]
you can use datediff for that
select count(*), extract(day from created_at) current_day
from posts
where datediff(now(), created_at) <= 7
group by current_day;
If you want 0 values for other day, then you would have to use a left join on a fake table that generate last seven days.
select count(posts.created_at) as nb_occurence, c.a as number_of_day
from
(select b.a
from (select 1 a
union all select 2 a
union all select 3 a
union all select 4 a
union all select 5 a
union all select 6 a
union all select 7 a) b) c
left join posts on extract(day from posts.created_at) % 7 - c.a in (select extract(day from date_sub(created_at, INTERVAL 7 DAY)) % 7 from posts)
group by c.a;
SELECT COUNT(*)
FROM posts
WHERE created_at<xy
GROUP BY created_at
SELECT COUNT(*), DATE_FORMAT(created_at,'%a')
FROM posts
WHERE created_at <= NOW() AND created_at >= DATE_SUB(created_at, INTERVAL 7 DAY)
GROUP BY DATE_FORMAT(created_at,'%a')
To add 0 for weekday having null in count:
SELECT a.weekday, IFNULL(b.total,0) FROM
(SELECT 'Mon' as weekday from dual union SELECT 'Tue' as weekday from dual union SELECT 'Wed' as weekday from dual union SELECT 'Thu' as weekday from dual union SELECT 'Fri' as weekday from dual union SELECT 'Sat' as weekday from dual union SELECT 'Sun' as weekday from dual) a
LEFT JOIN
(SELECT COUNT(*) as total, DATE_FORMAT(created_at,'%a') as weekday
FROM posts
WHERE created_at <= NOW() AND created_at >= DATE_SUB(created_at, INTERVAL 7 DAY)
GROUP BY DATE_FORMAT(created_at,'%a')) b on a.weekday=b.weekday

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;