A recursive query to get data by per month basis - mysql

how would you write a recursive query that can bring back data grouped by months?
As an example, something like this...
Month Amount
Jan £1000
Feb £1500
March £2000
Currently I'm running 3 separate queries that calculates the sum of all transactions within a month and then groups them and then I union everything. This however doesn't factor in coming months or previous months. It's hardcoded per se.
What I'm doing is the following for every month.
WHERE processing_time >= 2019-02-01 00:00:00
AND processing_time <= 2019-02-28 23:59:59
I need something that can show me 12 months worth of data but that also changes dynamically.
So March 2018 to Feb 2018 if I checked today and April 2018 to March 2019 if I check next month.

You don't have to solve it with recursive queries. you can use a "calendar" table to fill in the missing values.
A MySQL/MariaDB number generator comes to mind for month name generations.
SELECT
MONTHNAME(CURRENT_DATE + INTERVAL number_generator.number MONTH)
FROM (
SELECT
#row := #row + 1 AS number
FROM (
SELECT 0 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
) row1
CROSS JOIN (
SELECT 0 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
) row2
CROSS JOIN (
SELECT #row := -1
) init_user_params
) AS number_generator
WHERE
number_generator.number BETWEEN 0 AND 12
ORDER BY
number_generator.number ASC
Result
| MONTHNAME(CURRENT_DATE + INTERVAL number_generator.number MONTH) |
| ---------------------------------------------------------------- |
| March |
| April |
| May |
| June |
| July |
| August |
| September |
| October |
| November |
| December |
| January |
| February |
| March |
see demo
Edit
This is very close but can I also get the start and end days of each
month?
The last day is not that hard because there is a function for it in MySQL.
For the first day you need to be more creative with LAST_DAY() in combination with intervals like +1 DAY and -1 MONTH to get the first day of the month.
SELECT
MONTHNAME(CURRENT_DATE + INTERVAL number_generator.number MONTH)
, ((LAST_DAY(CAST(CURRENT_DATE + INTERVAL number_generator.number MONTH AS DATE)))
+ INTERVAL 1 DAY) - INTERVAL 1 MONTH AS first_day_of_month
, (LAST_DAY(CAST(CURRENT_DATE + INTERVAL number_generator.number MONTH AS DATE))) AS last_day_of_month
Results
| MONTHNAME(CURRENT_DATE + INTERVAL number_generator.number MONTH) | first_day_of_month | last_day_of_month |
| ---------------------------------------------------------------- | ------------------ | ----------------- |
| March | 2019-03-01 | 2019-03-31 |
| April | 2019-04-01 | 2019-04-30 |
| May | 2019-05-01 | 2019-05-31 |
| June | 2019-06-01 | 2019-06-30 |
| July | 2019-07-01 | 2019-07-31 |
| August | 2019-08-01 | 2019-08-31 |
| September | 2019-09-01 | 2019-09-30 |
| October | 2019-10-01 | 2019-10-31 |
| November | 2019-11-01 | 2019-11-30 |
| December | 2019-12-01 | 2019-12-31 |
| January | 2020-01-01 | 2020-01-31 |
| February | 2020-02-01 | 2020-02-29 |
| March | 2020-03-01 | 2020-03-31 |
see demo

Related

How to count the number of first transactions of each user per day?

I have a table named transactions which contains all transactions. Something like this:
// transactions
+--------+-------------+---------+-------------+---------------------+
| id | business_id | user_id | amount | created_at |
+--------+-------------+---------+-------------+---------------------+
| 1 | 503 | 12 | 4500 | 2021-04-15 04:22:09 |
| 2 | 33 | 12 | 1200 | 2021-04-17 12:22:10 |
| 3 | 771 | 13 | 400 | 2021-04-18 13:02:18 |
| 4 | 86 | 14 | 7500 | 2021-04-18 16:07:12 |
| 5 | 772 | 13 | 3400 | 2021-04-23 07:11:04 |
| 6 | 652 | 14 | 900 | 2021-04-24 10:35:08 |
| 7 | 567 | 15 | 1000 | 2021-04-24 14:55:11 |
+--------+-------------+---------+-------------+---------------------+
I need to count the number of users that have had their first transaction per day. So here is the expected result:
// The expected result
+------+-------+-------+--------------------------+
| year | month | day | first_transactions_num |
+------+-------+-------+--------------------------+
| 2021 | 04 | 15 | 1 |
| 2021 | 04 | 18 | 2 |
| 2021 | 04 | 24 | 1 |
+------+-------+-------+--------------------------+
Any idea how can I do that?
Here's what I've tried:
SELECT year(created_at) year,
month(created_at) month,
day(created_at) day,
count(1) num
FROM transactions
GROUP BY year, month, day
But my query doesn't care about "the first transaction of each user".
You should aggregate on the results of this query:
SELECT MIN(created_at) created_at
FROM transactions
GROUP BY user_id
which returns the 1st transaction of each user.
So do it like this:
SELECT YEAR(created_at) year,
MONTH(created_at) month,
DAY(created_at) day,
COUNT(*) num
FROM (
SELECT MIN(created_at) created_at
FROM transactions
GROUP BY user_id
) t
GROUP BY year, month, day
Or better:
SELECT YEAR(created_at) year,
MONTH(created_at) month,
DAY(created_at) day,
COUNT(*) num
FROM (
SELECT DATE(MIN(created_at)) created_at
FROM transactions
GROUP BY user_id
) t
GROUP BY created_at
Or if your version of MySql is 8.0+ use COUNT() window function:
SELECT DISTINCT
YEAR(MIN(created_at)) year,
MONTH(MIN(created_at)) month,
DAY(MIN(created_at)) day,
COUNT(*) OVER (PARTITION BY DATE(MIN(created_at))) num
FROM transactions
GROUP BY user_id
See the demo.

MySQL Query to get the monthly data difference

select * from new_joiner;
+------+--------------+
| id | date_of_join |
+------+--------------+
| 1 | 2020-01-10 |
| 2 | 2020-01-02 |
| 3 | 2020-01-05 |
| 4 | 2020-02-10 |
| 5 | 2020-02-11 |
| 6 | 2020-07-11 |
| 7 | 2020-07-11 |
| 8 | 2020-07-11 |
| 9 | 2020-07-11 |
| 10 | 2020-07-11 |
| 11 | 2020-05-01 |
| 12 | 2020-05-02 |
| 13 | 2020-05-03 |
| 14 | 2020-05-04 |
| 15 | 2020-05-05 |
| 16 | 2020-05-05 |
| 17 | 2020-05-06 |
+------+--------------+
select MONTHNAME(date_of_join) as MONTHNAME,
count(id) as JOINEE
from new_joiner
where MONTH(date_of_join)>=1
group by MONTH(date_of_join);
+-----------+--------+
| MONTHNAME | JOINEE |
+-----------+--------+
| January | 3 |
| February | 2 |
| May | 7 |
| July | 5 |
+-----------+--------+
I want a query that gives me the monthly data change compare to previous month.
For example: new joinee in Jan was 3, and in Feb it was 2, so compare to Jan in Feb month -1 joined, so the query should output me:
+-----------+-------------+
| MONTHNAME | JOINEE_DIFF |
+-----------+-------------+
| February | -1 |
| Mar | -2 |
| April | 0 |
| May | 7 |
| June | -7 |
| July | 5 |
| Aug | -5 |
| Sep | 0 |
| Oct | 0 |
| Nov | 0 |
| Dec | 0 |
+-----------+-------------+
Ignore Jan as it doesn't have a previous month and assume we have data only for a given year say 2020. Require data for all months from Feb to Dec.
Assuming you have data for every month, you can use lag():
select MONTHNAME(date_of_join) as MONTHNAME,
count(id) as JOINEE,
(count(*) - lag(count(*)) over (order by min(date_of_join)) as diff
from new_joiner
where MONTH(date_of_join) >= 1
group by MONTH(date_of_join);
Note that using months without years if fraught with peril. Also, the month() of any well-formed date should be larger than 1.
All this suggests a query more like:
select *
from (select MONTHNAME(date_of_join) as MONTHNAME,
count(id) as JOINEE,
(count(*) - lag(count(*)) over (order by min(date_of_join)) as diff,
min(date_of_join) as min_date_of_join
from new_joiner
where date_of_join >= '2020-01-01' and date_of_join < '2021-01-01'
group by MONTH(date_of_join)
) t
where diff is not null
order by min_date_of_join;
Use a correlated subquery to get the number of joinees of previous month and subtract it:
SELECT
t.monthname,
joinee - (SELECT COUNT(*) FROM new_joiner WHERE MONTH(date_of_join) = t.month - 1) JOINEE_DIFF
FROM (
SELECT MONTH(date_of_join) month, MONTHNAME(date_of_join) monthname,
COUNT(id) joinee
FROM new_joiner
GROUP BY month, monthname
) t
WHERE t.month > 1;

Mysql Counting different column with group by

I have a table where each row represent a correction with an id, date and a causes
id | date | causes
___________________________________
1 | 2018-01-29 08:49:19 | crash
2 | 2018-08-08 10:03:37 | timeout
3 | 2018-06-26 07:48:12 | other
I use this sql request to get the number of correction by month
SELECT YEAR(date) Year, MONTH(date) Month, count(*) Total FROM correction group by YEAR(date), MONTH(date)
It gives me a result like this
Year | Month | Total
____________________
2018 | 1 | 42
2018 | 2 | 69
2018 | 3 | 50
Is it possible to modify the request to also get the number of result for each causes and have a result like :
Year | Month | Total | crash | timeout | other
____________________________________________
2018 | 1 | 42 | 10 | 12 | 20
2018 | 2 | 69 | 9 | 50 | 10
2018 | 3 | 50 | 10 | 20 | 20
You need conditional aggregation :
SELECT YEAR(date) Year, MONTH(date) Month, COUNT(*) Total,
SUM(causes = 'crash') crash,
SUM(causes = 'timeout') timeout,
SUM(causes = 'other') other
FROM correction c
GROUP BY YEAR(date), MONTH(date);

Count() does not return zero MySQL

I am a web developer and tried to search for the solution on a mysql query.I am unable to get the right solution for the count() function to return zero.
The count() function doesnot return zero for all dates.
The query is as below . can anyone help me on this.
SELECT
count(stat_id) as typeSuccess,
device_type as typeName,
YEARWEEK(date_auth) as date
FROM auth_stat
WHERE
AUTH_RESULT = 'SUCCESS' AND
date_auth BETWEEN DATE_SUB(CURDATE(), INTERVAL 6 WEEK) AND CURDATE()
GROUP BY YEARWEEK(date_auth), device_type
ORDER BY YEARWEEK(date_auth)
The query that i tried to form is
select
date_auth,
count(stat_id) as typeSuccess,
device_type as typeName,
YEARWEEK(date_auth) as date
from
(
select #curDate := Date_Add(#curDate, interval 1 day) as MyJoinDate
from
(
select #curDate := CURDATE()
) sqlvars, auth_stat limit 18
) dateAll
LEFT JOIN auth_stat U on dateAll.MyJoinDate = U.date_auth
group by dateAll.MyJoinDate
Actual ouput :
+------------+-------------+
| date | typeSuccess |
+------------+-------------+
| 2015-03-18 | 11 |
+------------+-------------+
Expected Output:
+------------+-------------+
| date | typeSuccess |
+------------+-------------+
| 2015-03-18 | 11 |
| 2015-03-19 | 0 |
| 2015-03-20 | 0 |
+------------+-------------+
I believe all you want to do is just this
SELECT
SUM(AUTH_RESULT='SUCCESS') as numSuccess,
YEARWEEK(date_auth) as date
FROM auth_stat
WHERE date_auth BETWEEN DATE_SUB(CURDATE(), INTERVAL 6 WEEK) AND CURDATE()
GROUP BY YEARWEEK(date_auth), device_type
ORDER BY YEARWEEK(date_auth)
basically all i'm doing is telling MySQL to sum up the boolean value (0 or 1) when auth_result is a success.. SUM() will return a 0 for a particular week if there is no successes in that week
The main issue is you were filtering out all non successful auth_results which would then not be counted. so remove that from the where and you should be good!
if you want it per day then you can do this
SELECT
SUM(AUTH_RESULT='SUCCESS') as numSuccess,
DATE(date_auth) as date
FROM auth_stat
WHERE date_auth BETWEEN DATE_SUB(CURDATE(), INTERVAL 6 WEEK) AND CURDATE()
GROUP BY DATE(date_auth), device_type
ORDER BY DATE(date_auth)
SQLFIDDLE FOR BOTH RESULTS
try
SELECT COUNT(statid), dates.auth_date
FROM
( SELECT DISTINCT auth_date
FROM Table1
WHERE auth_date BETWEEN '2015-03-06' AND '2015-03-09' -- or whatever
) AS dates
LEFT JOIN Table1 ON TRUE
AND Table1.auth_date = dates.auth_date
AND table1.auth_result = 'SUCCESS'
GROUP BY dates.auth_date
ORDER BY dates.auth_date
as desribed here: http://sqlfiddle.com/#!9/84faf/2
for eg data:
+--------+-------------+------------+
| statid | auth_result | auth_date |
+--------+-------------+------------+
| 1 | SUCCESS | 2015-03-05 |
| 2 | SUCCESS | 2015-03-05 |
| 3 | SUCCESS | 2015-03-05 |
| 4 | OTHER | 2015-03-06 |
| 5 | OTHER | 2015-03-06 |
| 6 | OTHER | 2015-03-06 |
| 7 | SUCCESS | 2015-03-07 |
| 8 | SUCCESS | 2015-03-07 |
| 9 | SUCCESS | 2015-03-07 |
| 10 | OTHER | 2015-03-08 |
| 11 | SUCCESS | 2015-03-08 |
| 12 | OTHER | 2015-03-08 |
| 13 | SUCCESS | 2015-03-09 |
| 14 | SUCCESS | 2015-03-09 |
| 15 | SUCCESS | 2015-03-09 |
| 16 | OTHER | 2015-03-10 |
| 17 | OTHER | 2015-03-10 |
| 18 | SUCCESS | 2015-03-10 |
| 19 | OTHER | 2015-03-11 |
+--------+-------------+------------+
eg output:
+---------------+-------------------------+
| count(statid) | auth_date |
+---------------+-------------------------+
| 0 | March, 06 2015 00:00:00 |
| 3 | March, 07 2015 00:00:00 |
| 1 | March, 08 2015 00:00:00 |
| 3 | March, 09 2015 00:00:00 |
+---------------+-------------------------+
It is not beautiful but hope it helps:
SELECT
date_auth,
count(stat_id) as typeSuccess,
device_type as typeName,
YEARWEEK(date_auth) as date
FROM
(
SELECT
DATE(DATE_ADD(NOW(), interval tmp.id day)) AS MyJoinDate
FROM (SELECT 1 as id
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
) AS tmp
) dateAll
LEFT JOIN auth_stat U on dateAll.MyJoinDate = U.date_auth
group by dateAll.MyJoinDate
I have tried adding the query as below. It stills returns me records that have the counts and not the dates with the count as zero.
SELECT COUNT(stat_id), dates.date_auth
FROM
( SELECT DISTINCT date_auth
FROM auth_stat
WHERE date_auth BETWEEN DATE_SUB(CURDATE(), INTERVAL 20 DAY) AND CURDATE()
) AS dates
LEFT JOIN auth_stat ON TRUE
AND auth_stat.date_auth = dates.date_auth
AND auth_stat.auth_result = 'SUCCESS'
GROUP BY dates.date_auth
ORDER BY dates.date_auth
Output result for the above query:
COUNT(stat_id) date_auth Ascending 1
1 2015-03-05 00:00:00
1 2015-03-06 00:00:00
11 2015-03-18 00:00:00

MYSQL : How to select every YEAR_MONTH between two dates?

What I want to do :
I have a table like this :
TABLE mytable
- ID (INT)
- START (DATETIME)
- END (DATETIME)
Let's say I have these rows :
| ID | START | END |
|--------------------------------------------------
| 1 | 2014-01-02 00:00:00 | 2014-12-02 00:00:00 | => month between : 12
| 2 | 2014-01-03 00:00:00 | 2015-02-03 00:00:00 | => month between : 14
Note : the "month between" include the start and end months
I for each YEAR_MONTH between START and END, I want to display a row like this :
ID | MONTH | YEAR
---------------------
1 | 1 | 2014
1 | 2 | 2014
1 | 3 | 2014
1 | 4 | 2014
1 | 5 | 2014
1 | 6 | 2014
1 | 7 | 2014
1 | 8 | 2014
1 | 9 | 2014
1 | 10 | 2014
1 | 11 | 2014
1 | 12 | 2014
2 | 1 | 2014
2 | 2 | 2014
2 | 3 | 2014
2 | 4 | 2014
2 | 5 | 2014
2 | 6 | 2014
2 | 7 | 2014
2 | 8 | 2014
2 | 9 | 2014
2 | 10 | 2014
2 | 11 | 2014
2 | 12 | 2014
2 | 1 | 2015
2 | 2 | 2015
So 12 records for ID 1 and 14 for ID 2.
I'm a bit stuck when the number of month is > 12
WHERE I AM :
I'm doing this :
SELECT mytable.id,
months.id as month,
YEAR(start) as year
FROM mytable
/* Join on a list from 1 to 12 */
LEFT JOIN (SELECT 1 as id 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 UNION SELECT 12)
as months ON months.id BETWEEN MONTH(start) AND MONTH(end)
order by mytable.id, month, year
So ID 2 only has 2 rows for month 1 and 2 :
ID | MONTH | YEAR
---------------------
1 | 1 | 2014
1 | 2 | 2014
1 | 3 | 2014
1 | 4 | 2014
1 | 5 | 2014
1 | 6 | 2014
1 | 7 | 2014
1 | 8 | 2014
1 | 9 | 2014
1 | 10 | 2014
1 | 11 | 2014
1 | 12 | 2014
2 | 1 | 2014
2 | 2 | 2014
Do you have any ideas or advices for this problem ?
Is there a way to extract every YEAR_MONTH between two dates ?
Thank you.
HELPER :
Here is a script to create the table and insert the 2 rows mentionned :
CREATE TABLE mytable (
id INT PRIMARY KEY auto_increment,
start DATETIME NOT NULL,
end DATETIME NOT NULL
);
INSERT INTO mytable (start,end) VALUES
("2014-01-02 00:00:00","2014-12-02 00:00:00"),
("2014-01-03 00:00:00","2015-02-03 00:00:00");
If I understand you correctly, you need a table with dates (year - month) between each start and end date.
There's no simple select statement that will give you this, but you can create a procedure to do it. You need to create a temporary table, fill it with the values you need and then output the result.
Here's my proposed solution (considering a permanent table):
SQL Fiddle
MySQL 5.5.32 Schema Setup:
CREATE TABLE mytable (
id INT PRIMARY KEY auto_increment,
start DATETIME NOT NULL,
end DATETIME NOT NULL
)//
INSERT INTO mytable (start,end) VALUES
("2014-01-02 00:00:00","2014-12-02 00:00:00"),
("2014-01-03 00:00:00","2015-02-03 00:00:00")//
create procedure year_month_table()
begin
-- Declare the variables to fill the years_months table
declare id int;
declare start_date, end_date, d date;
-- Declare the "done" variable for the loop that fills the table,
-- the cursor to read the data, and the handler to check if the
-- loop should end.
declare done int default false;
declare cur_mytable cursor for
select * from mytable;
declare continue handler for not found
set done = true;
-- Create the table to hold your data
create table if not exists years_months (
row_id int unsigned not null auto_increment primary key,
id int not null,
month int,
year int,
unique index dedup(id, year, month),
index idx_id(id),
index idx_year(year),
index idx_month(month)
);
-- Open the cursor to read the ids and the start and end dates for each one
open cur_mytable;
-- Disable the indexes to speed up insertion
alter table years_months disable keys;
-- Start the loop
loop_data: loop
-- Read the values from your table and store them in the variables
fetch cur_mytable into id, start_date, end_date;
-- If you've reached the end of the table, then you must exit the loop
if done then
leave loop_data;
end if;
-- Initialize the date to fill the table
set d = start_date;
while d <= end_date do
-- Insert the values in your table
insert ignore into years_months (id, month, year) values (id, month(d), year(d));
-- Increment the d variable in 1 month
set d = date_add(d, interval +1 month);
end while;
end loop;
close cur_mytable;
-- Enable the indexes again
alter table years_months enable keys;
-- Show the result
select * from years_months;
end //
Query 1:
select * from mytable
Results:
| ID | START | END |
|----|--------------------------------|---------------------------------|
| 1 | January, 02 2014 00:00:00+0000 | December, 02 2014 00:00:00+0000 |
| 2 | January, 03 2014 00:00:00+0000 | February, 03 2015 00:00:00+0000 |
Query 2:
call year_month_table()
Results:
| ROW_ID | ID | MONTH | YEAR |
|--------|----|-------|------|
| 1 | 1 | 1 | 2014 |
| 2 | 1 | 2 | 2014 |
| 3 | 1 | 3 | 2014 |
| 4 | 1 | 4 | 2014 |
| 5 | 1 | 5 | 2014 |
| 6 | 1 | 6 | 2014 |
| 7 | 1 | 7 | 2014 |
| 8 | 1 | 8 | 2014 |
| 9 | 1 | 9 | 2014 |
| 10 | 1 | 10 | 2014 |
| 11 | 1 | 11 | 2014 |
| 12 | 1 | 12 | 2014 |
| 13 | 2 | 1 | 2014 |
| 14 | 2 | 2 | 2014 |
| 15 | 2 | 3 | 2014 |
| 16 | 2 | 4 | 2014 |
| 17 | 2 | 5 | 2014 |
| 18 | 2 | 6 | 2014 |
| 19 | 2 | 7 | 2014 |
| 20 | 2 | 8 | 2014 |
| 21 | 2 | 9 | 2014 |
| 22 | 2 | 10 | 2014 |
| 23 | 2 | 11 | 2014 |
| 24 | 2 | 12 | 2014 |
| 25 | 2 | 1 | 2015 |
| 26 | 2 | 2 | 2015 |
Notice that that last select statement in the procedure is the one that outputs the result. You can execute it every time you need.
Hope this helps
Important: As pointed by #amaster in his comment, this answer will fail if the period spans more than two years.
(Use the following code under your own risk ;) )
I've found another way to do this, but it's not a simple select statement and I think it's prone to errors, but I will put it here anyway:
select mytable.id, month, year
from mytable,
(select month, year
from
(select 1 as month
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
union select 12) as a,
(select year(start) as year from mytable
union select year(end) as year from mytable) as b) as a
where cast(concat_ws('-', a.year, a.month, day(mytable.start)) as date)
between date(mytable.start) and date(mytable.end)
order by mytable.id, year, month;
See this other SQL fiddle.
I know I am late to the party, but I was needing a good solution and sequencing was not working for my db version.
I started with https://stackoverflow.com/a/14813173/1707323 and made a few changes to get it working for my use like in this OP.
SELECT
DATE_FORMAT(m1, '%c') AS month_single,
DATE_FORMAT(m1, '%Y') AS this_year
FROM
(
SELECT
'2017-08-15' +INTERVAL m MONTH AS m1
FROM
(
SELECT
#rownum:=#rownum+1 AS m
from
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t1,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t2,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t3,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t4,
(SELECT #rownum:=-1) t0
) d1
) d2
WHERE
m1<='2020-03-23'
ORDER BY m1
This will get all of the months between these two dates. Please notice that the start date is in the second select clause and the end date is in the final where clause. This will include the starting month and ending month as well. It could be easily modified to exclude the starting and ending months with some extra +/- INTERVALS.