mySQL - Generate Additional Lines Between Dates [duplicate] - mysql

This question already has answers here:
MySQL how to fill missing dates in range?
(6 answers)
Closed 6 years ago.
I have a table like the below.
id month duration
001 1/1/16 3
002 3/1/16 4
003 12/1/15 2
I would like to add a new row to the table for every month after the month shown for the number specified minus 1, e.g. below:
id month duration
001 1/1/16 3
001 2/1/16 3
001 3/1/16 3
002 3/1/16 4
002 4/1/16 4
002 5/1/16 4
002 6/1/16 4
003 12/1/15 2
003 1/1/16 2
And so on, while duplicating the values in any column not shown.
I have done this in R, where I first populated 'short' data and then reshaped it to long, but after searching online, I still have no idea how to do this in mySQL. Thanks in advance for your help!

If you could automatically generate rows in MySQL that would be pretty easy; except that you can't. Still, you could use the technique described here to generate some rows and then use a JOIN to get the data you need:
-- Create a VIEW with 16 dummy rows
CREATE OR REPLACE VIEW generator_16
AS SELECT 0 n 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;
Having this generator, your query becomes:
SELECT id, date_add(month, INTERVAL n MONTH), duration
FROM tbl
INNER JOIN generator_16 ON n < duration
ORDER BY id, month
I assumed your table structure is the following:
CREATE TABLE tbl(id varchar(10), month date, duration int);
INSERT INTO tbl VALUES('001', '2016-01-01', 3);
INSERT INTO tbl VALUES('002', '2016-03-01', 4);
INSERT INTO tbl VALUES('003', '2015-12-01', 2);

Issues of data display are generally best resolved in the presentation layer (e.g. a simple PHP loop), but just for fun...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,dt DATE NOT NULL
,duration INT NOT NULL
);
INSERT INTO my_table VALUES
(1,'2016-01-01',3),
(2,'2016-03-01',4),
(3,'2015-12-01',2);
SELECT * FROM my_table;
+----+------------+----------+
| id | dt | duration |
+----+------------+----------+
| 1 | 2016-01-01 | 3 |
| 2 | 2016-03-01 | 4 |
| 3 | 2015-12-01 | 2 |
+----+------------+----------+
SELECT * FROM ints;
+---+
| i |
+---+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
+---+
SELECT x.id,x.dt + INTERVAL y.i MONTH,x.duration FROM my_table x JOIN ints y ON y.i < x.duration;
+----+---------------------------+----------+
| id | x.dt + INTERVAL y.i MONTH | duration |
+----+---------------------------+----------+
| 1 | 2016-01-01 | 3 |
| 1 | 2016-02-01 | 3 |
| 1 | 2016-03-01 | 3 |
| 2 | 2016-03-01 | 4 |
| 2 | 2016-04-01 | 4 |
| 2 | 2016-05-01 | 4 |
| 2 | 2016-06-01 | 4 |
| 3 | 2015-12-01 | 2 |
| 3 | 2016-01-01 | 2 |
+----+---------------------------+----------+

Related

SQL: Create Column (MySQL/PHPMyAdmin)

With the query:
SELECT TableA.ID, TableA.SensorID, TableA.Value, SensorIDs.Name, TableA.timestamp
FROM TableA
JOIN SensorIDs
ON TableA.SensorID = SensorIDs.ID // column 'Name' is in 'SensorIDs'
My result table looks like this:
ID | SensorID | Value | Name | timestamp
1 | 1 | 5 | A | 1000
2 | 2 | 10 | B | 1000
3 | 3 | 0 | C | 1000
4 | 1 | 1 | A | 2000
5 | 2 | 2 | B | 2000
6 | 3 | 6 | C | 2000
[..]
Is there a way to change my SQL query to get a table like this:
A | B | C | timestamp
5 | 10 | 0 | 1000
1 | 2 | 6 | 2000
Something with GROUP BY maybe?
EDIT: In the forseeable future there will be only these 3 values for 'Name'.
EDIT: RDBMS: MySQL-native (InnoDB), PHPMyAdmin
EDIT: Forgot to add column "SensorID" in the result.
I found the answer, by creating a PIVOT table with the tutorial I found here:
https://ubiq.co/database-blog/how-to-create-pivot-table-in-mysql/
SELECT time,
sum(IF(SensorID=1, Value, NULL)) AS Sensor1,
sum(IF(SensorID=2, Value, NULL)) AS Sensor2,
sum(IF(SensorID=3, Value, NULL)) AS Sensor3,
sum(IF(SensorID=4, Value, NULL)) AS Sensor4
FROM TableA
GROUP BY time

How to write loop in MySQL between two values?

I have table:
T1
| Name | score |
| John | 1 |
|Anton | 2 |
|George| 8 |
|Peter | 1 |
| Tom | 2 |
I need to loop over table between two values.
I need to create a procedure or a function that for given value1 and value2 prints out (in alphabetical order) names that have in total(score) more than value1 and less than value2.
EXAMPLE
For value1= 5, and value2=12.
First i need to sort names alphabetically.
| Name | score |
|Anton | 2 |
|George| 8 |
| John | 1 |
|Piter | 1 |
| Tom | 2 |
Since value1 is 5, and value2 is 12, i need to loop until sum(score) will be more than 5, and less than 12. In this case result should be:
| Name |
|Anton |
|George|
| John |
because in total they got more than 5 and less than 12 score.
1)discard scores > 12 2) workout cumulative sum 3) work out max cumulative sum in the required range 4) select those there cumulative sum is < max cumulative sum
DROP table if exists t;
create table t
( Name varchar(10), score int);
insert into t values
('Anton' , 12),
('George', 12),
('John' , 1),
('Piter' , 1),
('Tom' , 2),
('bilal' , 5)
;
with
cte as
(select t.*,
sum(score) over (order by name) cumsum
from t
where score < 12) ,
cte1 as
(select max(cumsum) maxcumsum from cte where cumsum < 12 and cumsum > 5),
cte2 as
(select cte.*,cte1.maxcumsum
from cte
cross join cte1)
select * from cte2
where maxcumsum > 5 and cumsum <= maxcumsum
order by name;
+-------+-------+--------+-----------+
| Name | score | cumsum | maxcumsum |
+-------+-------+--------+-----------+
| bilal | 5 | 5 | 9 |
| John | 1 | 6 | 9 |
| Piter | 1 | 7 | 9 |
| Tom | 2 | 9 | 9 |
+-------+-------+--------+-----------+
4 rows in set (0.022 sec)

How can I select the next available value in a column taking into account another column in SQL?

I have the following SQL table:
bid btype Name world vi
---|----|------------ |--------|---------
1 | 1 | Business 1 | 0 | 44
2 | 4 | Business 2 | 0 | 55
5 | 5 | Business 3 | 0 | 23
3 | 1 | Business 4 | 1 | 99
4 | 2 | Business 5 | 0 | 12
6 | 3 | Business 6 | 0 | 14
7 | 2 | Business 7 | 1 | 55
8 | 1 | Business 8 | 2 | 66
9 | 2 | Business 9 | 2 | 77
10 | 1 | Business 10 | 3 | 88
What I want is to gradually increase the value in the "world" column according to its "btype", for example every single row starts with a value 0 in the "world column
as it's the first time such value in the "btype" column is inserted, what I want is to check if there's already a "btype" inserted so that the "world" column no longer will assume a value of 0 but 1 and so on...
What I want to achieve is that there can't be two rows sharing the same "btype" with the same "world", the "btype" can be the same but not the "world", it has to be different and I want
it to gradually increase.
How would I approach to do such thing?
E.g.:
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(bid INT NOT NULL PRIMARY KEY
,btype INT NOT NULL
);
INSERT INTO my_table VALUES
( 1,1),
( 2,4),
( 5,5),
( 3,1),
( 4,2),
( 6,3),
( 7,2),
( 8,1),
( 9,2),
(10,1);
SELECT bid
, btype
, i
FROM
( SELECT x.*
, CASE WHEN #prev=btype THEN #i:=#i+1 ELSE #i:=0 END i
, #prev:=btype prev
FROM my_table x
,( SELECT #i:=0,#prev:=null) vars
ORDER
BY btype,bid
) n
ORDER
BY bid;
+-----+-------+------+
| bid | btype | i |
+-----+-------+------+
| 1 | 1 | 0 |
| 2 | 4 | 0 |
| 3 | 1 | 1 |
| 4 | 2 | 0 |
| 5 | 5 | 0 |
| 6 | 3 | 0 |
| 7 | 2 | 1 |
| 8 | 1 | 2 |
| 9 | 2 | 2 |
| 10 | 1 | 3 |
+-----+-------+------+
I don't usually do queries like this, so I can't say for certain, but something like this should maybe do the trick.
INSERT INTO theTable(btype, Name, world, vi)
SELECT [val1]
, [val2]
, IFNULL((SELECT MAX(world) FROM theTable WHERE btype = [val1]),-1)+1
, [val3]
;
You might even be able to just include the 3rd select expression in a conventional INSERT...VALUES value list; but like I said, I don't usually do queries like this. (I'm of the apparent minority that likes to check first before inserting; but not as a replacement for an appropriate uniqueness constraint.)

Mysql optimization and explode

I have the following query that displays the top 10 most drawn pairs of numbers from the whole
table
select
p, count(p) as frequency
from
(SELECT
id,
CASE power1 <= power2 WHEN TRUE THEN CONCAT(power1,"-",power2) ELSE CONCAT(power2,"-",power1)
END p
FROM power
UNION
SELECT
id,
CASE power1<=power3 WHEN TRUE THEN CONCAT(power1,"-",power3) ELSE CONCAT(power3,"-",power1) END p
FROM power
UNION
SELECT
id,
CASE power1<=power4 WHEN TRUE THEN CONCAT(power1,"-",power4) ELSE CONCAT(power4,"-",power1) END p
FROM power
UNION
...............................................
SELECT
id,
CASE power19<=power20 WHEN TRUE THEN CONCAT(power19,"-",power20) ELSE CONCAT(power20,"-",power19)
END p
FROM power) as b
group by
p
order by
frequency desc, p asc
limit
0, 10
How can I impose a limit to take just the first 100 lines in descending order by ID? The query would be like this:
ORDER BY id LIMIT 0,100
But I haven't been able to adapt it for the above.
Could the code be optimized more than that?
power1, Power2 are values from tables.... would it work if i would have a string like 3,4,5,6 and then explode "," and after that power1 becomes 3, power2 to become 4, etc?
I mean the table format to look something like this :
table2
LATER EDIT :
I have table like this :
Table: data
+----+----+-----+
| id | nr | set |
+----+----+-----+
| 1 | 52 | 1 |
| 2 | 47 | 1 |
| 3 | 4 | 1 |
| 4 | 3 | 1 |
| 5 | 77 | 1 |
| 6 | 71 | 1 |
| 7 | 6 | 1 |
| 8 | 41 | 1 |
| 9 | 15 | 1 |
| 10 | 79 | 1 |
| 11 | 35 | 2 |
| 12 | 50 | 2 |
| 13 | 16 | 2 |
| 14 | 1 | 2 |
| 15 | 32 | 2 |
| 16 | 77 | 2 |
| 17 | 30 | 2 |
| 18 | 7 | 2 |
| 19 | 20 | 2 |
| 20 | 28 | 2 |
| .. | .. | ... |
+----+----+-----+
I have like 34360 id
And the following query :
SELECT
`n1`.`nr` AS `num_1`,
`n2`.`nr` AS `num_2`,
COUNT(1) AS `total`
FROM (select * from data ORDER BY id DESC limit 0,1000) AS `n1`
JOIN `data` AS `n2`
ON `n1`.`set` = `n2`.`set` AND `n1`.`nr` < `n2`.`nr`
GROUP BY `n1`.`nr`, `n2`.`nr`
ORDER BY `total` DESC
LIMIT 20
And is working fine !
I would like to know how i can find out the pairs of numbers that have not been drawn together for the longest time. Per example:
1,42 (together, as a pair) has not been drawn for 24 draws
32,45-as a pair as well-has not been drawn for 22 draws
etc
Consider the following:
Un-normalised:
id power1 power2 power3 power4
1 4 9 10 16
2 6 12 15 19
3 2 4 6 7
4 3 8 15 17
5 2 10 11 14
6 4 10 12 19
7 1 4 9 11
Normalised:
id power value
1 1 4
1 2 9
1 3 10
1 4 16
2 1 6
2 2 12
2 3 15
2 4 19
3 1 2
3 2 4
3 3 6
3 4 7
4 1 3
4 2 8
4 3 15
4 4 17
5 1 2
5 2 10
5 3 11
5 4 14
6 1 4
6 2 10
6 3 12
6 4 19
7 1 1
7 2 4
7 3 9
7 4 11
So...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL
,power INT NOT NULL
,value INT NOT NULL
,PRIMARY KEY(id,power)
);
INSERT INTO my_table VALUES
(1,1,4),(1,2,9),(1,3,10),(1,4,16),
(2,1,6),(2,2,12),(2,3,15),(2,4,19),
(3,1,2),(3,2,4),(3,3,6),(3,4,7),
(4,1,3),(4,2,8),(4,3,15),(4,4,17),
(5,1,2),(5,2,10),(5,3,11),(5,4,14),
(6,1,4),(6,2,10),(6,3,12),(6,4,19),
(7,1,1),(7,2,4),(7,3,9),(7,4,11);
SELECT LEAST(x.value,y.value)a -- LEAST/GREATEST is only necessary in the event that
, GREATEST(x.value,y.value) b -- power1 value may be greater than powerN value
, COUNT(*) freq
FROM my_table x
JOIN my_table y
ON y.id = x.id
AND y.power < x.power
GROUP
BY LEAST(x.value, y.value) -- again only necessary if using LEAST/GREATEST above
, GREATEST(x.value,y.value)
ORDER
BY freq DESC
, a
, b;
+----+----+------+
| a | b | freq |
+----+----+------+
| 4 | 9 | 2 |
| 4 | 10 | 2 |
| 12 | 19 | 2 |
| 1 | 4 | 1 |
| 1 | 9 | 1 |
| 1 | 11 | 1 |
| 2 | 4 | 1 |
| 2 | 6 | 1 |
| 2 | 7 | 1 |
| 2 | 10 | 1 |
| 2 | 11 | 1 |
| 2 | 14 | 1 |
| 3 | 8 | 1 |
| 3 | 15 | 1 |
| 3 | 17 | 1 |
| 4 | 6 | 1 |
| 4 | 7 | 1 |
| 4 | 11 | 1 |
| 4 | 12 | 1 |
| 4 | 16 | 1 |
| 4 | 19 | 1 |
| 6 | 7 | 1 |
| 6 | 12 | 1 |
| 6 | 15 | 1 |
| 6 | 19 | 1 |
| 8 | 15 | 1 |
| 8 | 17 | 1 |
| 9 | 10 | 1 |
| 9 | 11 | 1 |
| 9 | 16 | 1 |
| 10 | 11 | 1 |
| 10 | 12 | 1 |
| 10 | 14 | 1 |
| 10 | 16 | 1 |
| 10 | 19 | 1 |
| 11 | 14 | 1 |
| 12 | 15 | 1 |
| 15 | 17 | 1 |
| 15 | 19 | 1 |
+----+----+------+
While I fully agree with #Strawberry about normalising your data, the following is an example of how to possibly do it with your current data structure (not tested).
SELECT CASE a.power_val <= b.power_val WHEN TRUE THEN CONCAT(a.power_val,"-",b.power_val) ELSE CONCAT(b.power_val,"-",a.power_val) END p,
COUNT(a.id) as frequency
FROM
(
SELECT id,1 AS power_col, power1 AS power_val FROM power UNION
SELECT id,2, power2 FROM power UNION
SELECT id,3, power3 FROM power UNION
SELECT id,4, power4 FROM power UNION
SELECT id,5, power5 FROM power UNION
SELECT id,6, power6 FROM power UNION
SELECT id,7, power7 FROM power UNION
SELECT id,8, power8 FROM power UNION
SELECT id,9, power9 FROM power UNION
SELECT id,10, power10 FROM power UNION
SELECT id,11, power11 FROM power UNION
SELECT id,12, power12 FROM power UNION
SELECT id,13, power13 FROM power UNION
SELECT id,14, power14 FROM power UNION
SELECT id,15, power15 FROM power UNION
SELECT id,16, power16 FROM power UNION
SELECT id,17, power17 FROM power UNION
SELECT id,18, power18 FROM power UNION
SELECT id,19, power19 FROM power UNION
SELECT id,20, power20 FROM power
ORDER BY id DESC
LIMIT 2000
) a
INNER JOIN
(
SELECT id, 1 AS power_col, power1 AS power_val FROM power UNION
SELECT id, 2, power2 FROM power UNION
SELECT id,3, power3 FROM power UNION
SELECT id,4, power4 FROM power UNION
SELECT id,5, power5 FROM power UNION
SELECT id,6, power6 FROM power UNION
SELECT id,7, power7 FROM power UNION
SELECT id,8, power8 FROM power UNION
SELECT id,9, power9 FROM power UNION
SELECT id,10, power10 FROM power UNION
SELECT id,11, power11 FROM power UNION
SELECT id,12, power12 FROM power UNION
SELECT id,13, power13 FROM power UNION
SELECT id,14, power14 FROM power UNION
SELECT id,15, power15 FROM power UNION
SELECT id,16, power16 FROM power UNION
SELECT id,17, power17 FROM power UNION
SELECT id,18, power18 FROM power UNION
SELECT id,19, power19 FROM power UNION
SELECT id,20, power20 FROM power
ORDER BY id DESC
LIMIT 2000
) b
ON a.id = b.id
AND a.power_col != b.power_col
GROUP BY p
ORDER BY frequency DESC, p ASC
LIMIT 0,10
Note using normalised data structures would likely be far quicker.
EDIT
Think something like the following might give you what you need.
The big sub query is to get every possible combination (idea is to also cope with pairs that have never been used), with the first number being smaller than the 2nd just for consistency. This is then joined against the tables of data to get the matching numbers and the respective id fields. Then uses MIN to get the smallest id:-
SELECT all_combo.num_1,
all_combo.num_2,
MIN(d1.id)
FROM
(
SELECT sub0.nr AS num_1,
sub1.nr AS num_2
FROM
(
SELECT DISTINCT nr
FROM data
) sub0
INNER JOIN
(
SELECT DISTINCT nr
FROM data
) sub1
WHERE sub0.nr < sub1.nr
) all_combo
LEFT OUTER JOIN data d1 ON all_combo.num_1
LEFT OUTER JOIN data d2 ON all_combo.num_2 AND d1.set = d2.set
GROUP BY all_combo.num_1,
all_combo.num_2

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.