How to get the next 12 future dates - mysql

I have a bit of code that tells you when a contact would not have been contacted and highlights that day on a calender. i.e. If you last contacted the person yesterday, then a highlighted day will appear one month from that on next months page.
What I would like to do is do that for every month up to a year. So in the previous example, if I moved one month beyond the highlighted day, it would be 'not contacted for two months', three months, four months and so fourth up to 12 months.
Here is what I'm using now for the 'not contacted for one month' query:
SELECT DATE_FORMAT(DATE(DATE_ADD(date, INTERVAL 1 MONTH)), '%Y-%c-%d') AS overDate
FROM contact_method_history
WHERE DATE(DATE_ADD(date, INTERVAL 1 MONTH)) = '$SQLDate'
AND entityRef = ".$this->entityId."
ORDER BY date DESC
LIMIT 1
$this->entityId could be something like 153 or 9045, its just a reference to the contact.
$SQLDate is in the form of '2012-05-09'.

I don't agree with comments: if you do a loop then you will run 12 queries instead of 1, which is decreasing performances. In the other hand, the statement for a "all in one" query is not trivial:
SELECT date
, i.m
, DATE_FORMAT(DATE(DATE_ADD(cmh.date, INTERVAL i.m MONTH)), '%Y-%c-%d') AS overDate
FROM contact_method_history AS cmh
, (
SELECT 1 as m
UNION ALL SELECT 2 as m
UNION ALL SELECT 3 as m
UNION ALL SELECT 4 as m
UNION ALL SELECT 5 as m
UNION ALL SELECT 6 as m
UNION ALL SELECT 7 as m
UNION ALL SELECT 8 as m
UNION ALL SELECT 9 as m
UNION ALL SELECT 10 as m
UNION ALL SELECT 11 as m
UNION ALL SELECT 12 as m
) AS i
WHERE ( DATE(DATE_ADD(cmh.date, INTERVAL i.m MONTH)) = '$SQLDate' )
AND (entityRef = ".$this->entityId.")
GROUP BY i.m, DATE(DATE_ADD(cmh.date, INTERVAL i.m MONTH))

Related

MYSQL SUM until last day of Each month for last 12 months

I have a table like this two
Table A
date amount B_id
'2020-1-01' 3000000 1
'2019-8-01' 15012 1
'2019-6-21' 90909 1
'2020-1-15' 84562 1
--------
Table B
id type
1 7
2 5
I have to show sum of amount until the last date of each month for the last 12 month.
The query i have prepared is like this..
SELECT num2.last_dates,
(SELECT SUM(amount) FROM A
INNER JOIN B ON A.B_id = B.id
WHERE B.type = 7 AND A.date<=num2.last_dates
),
(SELECT SUM(amount) FROM A
INNER JOIN B ON A.B_id = B.id
WHERE B.type = 5 AND A.date<=num2.last_dates)
FROM
(SELECT last_dates
FROM (
SELECT LAST_DAY(CURDATE() - INTERVAL CUSTOM_MONTH MONTH) last_dates
FROM(
SELECT 1 CUSTOM_MONTH UNION
SELECT 0 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 )num
) num1
)num2
ORDER BY num2.last_dates
This gives me the result like this which is exactly how i need it. I need this query to execute faster. Is there any better way to do what i am trying to do?
2019-05-31 33488.69 109.127800
2019-06-30 263.690 1248932.227800
2019-07-31 274.690 131.827800
2019-08-31 627.690 13.687800
2019-09-30 1533.370000 08.347800
2019-10-31 1444.370000 01.327800
2019-11-30 5448.370000 247.227800
2019-12-31 61971.370000 016.990450
2020-01-31 19550.370000 2535.185450
2020-02-29 986.370000 405.123300
2020-03-31 1152.370000 26.793300
2020-04-30 9404.370000 11894.683300
2020-05-31 3404.370000 17894.683300
I'd use conditional aggregation, and pre-aggregate the monthly totals in one pass, instead of doing twenty-six individual passes repeatedly through the same data.
I'd start with something like this:
SELECT CASE WHEN A.date < DATE(NOW()) + INTERVAL -14 MONTH
THEN LAST_DAY( DATE(NOW()) + INTERVAL -14 MONTH )
ELSE LAST_DAY( A.date )
END AS _month_end
, SUM(IF( B.type = 5 , B.amount , NULL)) AS tot_type_5
, SUM(IF( B.type = 7 , B.amount , NULL)) AS tot_type_7
FROM A
JOIN B
ON B.id = A.B_id
WHERE B.type IN (5,7)
GROUP
BY _month_end
(column amount isn't qualified in original query, so just guessing here which table that is from. adjust as necessary. best practice is to qualify all column references.
That gets us the subtotals for each month, in a single pass through A and B.
We can get that query tested and tuned.
Then we can incorporate that as an inline view in an outer query which adds up those monthly totals. (I'd do an outer join, just in case rows are missing, sow we don't wind up omitting rows.)
Something like this:
SELECT d.dt + INTERVAL -i.n MONTH + INTERVAL -1 DAY AS last_date
, SUM(IFNULL(t.tot_type_5,0)) AS rt_type_5
, SUM(IFNULL(t.tot_type_7,0)) AS rt_type_7
FROM ( -- first day of next month
SELECT DATE(NOW()) + INTERVAL -DAY(DATE(NOW()))+1 DAY + INTERVAL 1 MONTH AS dt
) d
CROSS
JOIN ( -- thirteen integers, integers 0 thru 12
SELECT 0 AS 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
) i
LEFT
JOIN ( -- totals by month
SELECT CASE WHEN A.date < DATE(NOW()) + INTERVAL -14 MONTH
THEN LAST_DAY( DATE(NOW()) + INTERVAL -14 MONTH )
ELSE LAST_DAY( A.date )
END AS _month_end
, SUM(IF( B.type = 5 , B.amount , NULL)) AS tot_type_5
, SUM(IF( B.type = 7 , B.amount , NULL)) AS tot_type_7
FROM A
JOIN B
ON B.id = A.B_id
WHERE B.type IN (5,7)
GROUP
BY _month_end
) t
ON t._month_end < d.dt
GROUP BY d.dt + INTERVAL -i.n MONTH + INTERVAL -1 DAY
ORDER BY d.dt + INTERVAL -i.n MONTH + INTERVAL -1 DAY DESC
The design is meant to do one swoop through the A JOIN B set. We're expecting to get about 14 rows back. And we're doing a semi-join, duplicating the oldest months multiple times, so approx . 14 x 13 / 2 = 91 rows, that get collapsed into 13 rows.
The big rock in terms of performance is going to be materializing that inline view query.
This is how I'd probably approach this in MySQL 8 with SUM OVER:
Get the last 12 months.
Use these months to add empty month rows to the original data, as MySQL doesn't support full outer joins.
Get the running totals for all months.
Show only the last twelve months.
The query:
with months (date) as
(
select last_day(current_date - interval 1 month) union all
select last_day(current_date - interval 2 month) union all
select last_day(current_date - interval 3 month) union all
select last_day(current_date - interval 4 month) union all
select last_day(current_date - interval 5 month) union all
select last_day(current_date - interval 6 month) union all
select last_day(current_date - interval 7 month) union all
select last_day(current_date - interval 8 month) union all
select last_day(current_date - interval 9 month) union all
select last_day(current_date - interval 10 month) union all
select last_day(current_date - interval 11 month) union all
select last_day(current_date - interval 12 month)
)
, data (date, amount, type) as
(
select last_day(a.date), a.amount, b.type
from a
join b on b.id = a.b_id
where b.type in (5, 7)
union all
select date, null, null from months
)
select
date,
sum(sum(case when type = 5 then amount end)) over (order by date) as t5,
sum(sum(case when type = 7 then amount end)) over (order by date) as t7
from data
group by date
order by date
limit 12;
Demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=ddeb3ab3e086bfc182f0503615fba74b
I don't know whether this is faster than your own query or not. Just give it a try. (You'd get my query much faster by adding a generated column for last_day(date) to your table and use this. If you need this often, this may be an option.)
You are getting some complicated answers. I think it is easier. Start with knowing we can easily sum for each month:
SELECT SUM(amount) as monthtotal,
type,
MONTH(date) as month,
YEAR(date) as year
FROM A LEFT JOIN B on A.B_id=B.id
GROUP BY type,month,year
From that data, we can use a variable to get running total. Best to do by initializing the variable, but not necessary. We can get the data necessary like this
SET #running := 0;
SELECT (#running := #running + monthtotal) as running, type, LAST_DAY(CONCAT(year,'-',month,'-',1))
FROM
(SELECT SUM(amount) as monthtotal,type,MONTH(date) as month,YEAR(date) as year FROM A LEFT JOIN B on A.B_id=B.id GROUP BY type,month,year) AS totals
ORDER BY year,month
You really need to have a connector that supports multiple statements, or make multiple calls to initialize the variable. Although you can null check the variable and default to 0, you still have an issue if you run the query a second time.
Last thing, if you really want the types to be summed separately:
SET #running5 := 0;
SET #running7 := 0;
SELECT
LAST_DAY(CONCAT(year,'-',month,'-',1)),
(#running5 := #running5 + (CASE WHEN type=5 THEN monthtotal ELSE 0 END)) as running5,
(#running7 := #running7 + (CASE WHEN type=7 THEN monthtotal ELSE 0 END)) as running7
FROM
(SELECT SUM(amount) as monthtotal,type,MONTH(date) as month,YEAR(date) as year FROM A LEFT JOIN B on A.B_id=B.id GROUP BY type,month,year) AS totals
ORDER BY year,month
We still don't show months where there is no data. I'm not sure that is a requirement. But this should only need one pass of table A.
Also, make sure the id on table B is indexed.

Return 0 as SUM result if no records are found

I'm trying to summarize the calories of activities per day for a specific user for the last 7 days (actual day is 7th day). There are tables user and activities and the mapping table user_activities.
The following example is for the user with id=1;
Sum up calories each day last 7 days (this day 7th day) */
SELECT
DATE(a.end_time), SUM(a.calories)
FROM
activities a
JOIN
user_activities uc ON uc.activity_id = a.id
WHERE
uc.user_id = 1
AND DATE(a.end_time) >= CURRENT_DATE - INTERVAL 6 DAY
GROUP BY
DATE(a.end_time) DESC
That query returns this result set:
2018-12-28 9600
2018-12-27 1200
2018-12-26 1200
2018-12-25 1200
2018-12-24 4800
2018-12-22 1200
Which is correct but now my problem is, as you can see in the list the 12-23-2018 is not listed because there are no activities on this date. Now I want to display
2018-12-23 0
instead of nothing.
How can I get the desired result?
Thanks for your help
I also tried with IFNULL and COALESCE but no luck so far
Sum up calories each day last 7 days (this day 7th day) */
SELECT
DATE(a.end_time), SUM(a.calories)
FROM
activities a
JOIN
user_activities uc ON uc.activity_id = a.id
WHERE
uc.user_id = 1
AND DATE(a.end_time) >= CURRENT_DATE - INTERVAL 6 DAY
GROUP BY
DATE(a.end_time) DESC
Result:
2018-12-28 9600
2018-12-27 1200
2018-12-26 1200
2018-12-25 1200
2018-12-24 4800
2018-12-22 1200
Expected result:
2018-12-28 9600
2018-12-27 1200
2018-12-26 1200
2018-12-25 1200
2018-12-24 4800
2018-12-23 0
2018-12-22 1200
Activities table:
If there's a chance your activities table doesn't have an activity listed on a particular day, then fa06's trick won't work out. A simple way to cover that case is to add some zero records on before you do your sum:
SELECT DATE(d), SUM(c)
FROM (
SELECT a.end_time as d, a.calories as c
FROM activities a
JOIN user_activities uc ON uc.activity_id = a.id
WHERE uc.user_id = 1
AND DATE(a.end_time) >= CURRENT_DATE - INTERVAL 6 DAY
UNION ALL
SELECT CURRENT_DATE, 0
UNION ALL
SELECT CURRENT_DATE - INTERVAL 1 DAY, 0
UNION ALL
SELECT CURRENT_DATE - INTERVAL 2 DAY, 0
UNION ALL
SELECT CURRENT_DATE - INTERVAL 3 DAY, 0
UNION ALL
SELECT CURRENT_DATE - INTERVAL 4 DAY, 0
UNION ALL
SELECT CURRENT_DATE - INTERVAL 5 DAY, 0
UNION ALL
SELECT CURRENT_DATE - INTERVAL 6 DAY, 0
) z
GROUP BY DATE(d)
fa06's method relies on there being one record every day in the activities table. It's a valid way to approach the problem, but you haven't been specific about what data these tables contain (hint: when posting a db question, do include sample data from each table)
With this method we do our lookup as normal, but we also generate 7 fake rows with dates from the last 7 days and a 0 calorie count. When added onto real calorie counts, these are fat free ;) and when there is no data for a particular day, these stand alone to provide the 0
If you want more days, consider moving to a row generating pattern. MySQL doesn't have row generators like other DBs, but the simplest trick is to create
variable, init it with 0 then increment and use it upon every row returned from a table with at least 30 rows:
SELECT CURRENT_DATE - INTERVAL (#row := #row + 1) DAY as dt, 0 as cal
FROM activities t, (SELECT #row := -1) r
LIMIT 30
The theory behind this is: the table has at least 30 rows, #row variable is inited to -1, and exists session-wide for the query. As rows are pulled out and returned, #row is incremented and then returned (so it's 0, 1, 2.. ) and this increasing count is used to subtract 0, 1, 2 etc days off of the current data, giving us a sequence of dates for the past 30 days
SELECT DATE(d), SUM(c)
FROM (
SELECT a.end_time as d, a.calories as c
FROM activities a
JOIN user_activities uc ON uc.activity_id = a.id
WHERE uc.user_id = 1
AND DATE(a.end_time) >= CURRENT_DATE - INTERVAL 29 DAY
UNION ALL
SELECT CURRENT_DATE - INTERVAL (#row := #row + 1) DAY as dt, 0 as cal
FROM activities t, (SELECT #row := -1) r
WHERE #row < 30
) z
GROUP BY DATE(d)
Note that I wasn't able to test either of these queries; the second might have some minor syntax error. If it turns out not to work, and the error is nontrivial/something you cant fix let me know.
Debugging:
Run each of these queries in isolation. I don't know how many rows this will produce:
SELECT a.end_time as d, a.calories as c
FROM activities a
JOIN user_activities uc ON uc.activity_id = a.id
WHERE uc.user_id = 1
AND DATE(a.end_time) >= CURRENT_DATE - INTERVAL 29 DAY
And this one should produce 30 rows. If it doesn't it will be because you didn't use a table with at least 30 rows:
SELECT CURRENT_DATE - INTERVAL (#row := #row + 1) DAY as dt, 0 as cal
FROM activities t, (SELECT #row := -1) r
LIMIT 30
Edit:
Fixed the bug with this - LIMIT was being applied after the union; this was giving undesired results
You can try below - using left join
SELECT DATE(a.end_time), SUM(a.calories)
FROM activities a
left JOIN user_activities uc ON uc.activity_id = a.id
Where uc.user_id = 1 AND DATE(a.end_time) >= CURRENT_DATE - INTERVAL 6 DAY
GROUP BY DATE(a.end_time) order by DATE(a.end_time) DESC
I'm trying it on my end and just updated the SUM(a.calories) to IF(SUM(a.calories) IS NULL, 0, SUM(a.calories))
Here you may try...
SELECT DATE(a.end_time), IF(SUM(a.calories) IS NULL, 0, SUM(a.calories))
FROM activities a
JOIN user_activities uc ON uc.activity_id = a.id
WHERE uc.user_id = 1
AND
DATE(a.end_time) >= CURRENT_DATE - INTERVAL 6 DAY
GROUP BY DATE(a.end_time) DESC
This query should to the trick :
the CTE dynamically generates a date range (this will go up to about 300 years ahead)
the result is LEFT JOINed with the activities and user_activities tables
the COALESCE function turns empty SUMs into 0 values
Query :
WITH v AS (
SELECT * FROM
(SELECT adddate('1970-01-01',t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) selected_date 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) t0,
(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) t1,
(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) t2,
(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) t3,
(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) t4) v
WHERE selected_date BETWEEN CURRENT_DATE - INTERVAL 6 DAY AND CURRENT_DATE
)
SELECT v.selected_date, COALESCE(SUM(a.calories), 0)
FROM
v
LEFT JOIN activities a on DATE(a.end_time) = v.selected_date
LEFT JOIN user_activities uc ON uc.activity_id = a.id AND uc.user_id = 1
GROUP BY DATE(a.end_time) DESC
ORDER BY v.selected_date

MySQL gett rows from last week with their day name

I am having trouble with a query. This is taken from a similar query where i count number of rows per month.
I want to count all rows for each day of the last week and display the day name and a count. If there are no rows for that day, display zero.
I know the UNIONS won't work but i don't know what to replace it with.
At the moment it get the last 7 days but the day name is NULL
SELECT DAYNAME(STR_TO_DATE(Days.ID, '%a')) AS `day`, COUNT(`returns`.list_date) AS `total`
FROM
(
SELECT 1 as ID UNION SELECT 2 as ID UNION SELECT 3 as ID UNION SELECT 4 as ID
UNION
SELECT 5 as ID UNION SELECT 6 as ID UNION SELECT 7 as ID
) as Days
WHERE (list_date >= DATE_SUB(NOW(), INTERVAL 1 WEEK))
GROUP BY Days.id
UPDATE:
I have created a SQL fiddle showing the code output from #Gordon Linoff answer below which doesn't get the counted rows
http://sqlfiddle.com/#!9/969463/1
One method for doing what you want is a LEFT JOIN:
SELECT DAYNAME(DATE_SUB(CURDATE(), INTERVAL Days.n DAY)) AS `day`,
COUNT(r.list_date) AS `total`
FROM (SELECT 1 as n UNION ALL SELECT 2 as n UNION ALL
SELECT 3 as n UNION ALL SELECT 4 as n UNION ALL
SELECT 5 as n UNION ALL SELECT 6 as n UNION ALL
SELECT 7 as n
) Days LEFT JOIN
returns r
ON r.list_date = DATE_SUB(CURDATE(), INTERVAL Days.n DAY))
GROUP BY Days.n
ORDER BY Days.n;
Some notes:
Use UNION ALL instead of UNION, unless you have a good reason for incurring the overhead of removing duplicates.
This assumes that returns.list_date is actually a date, because it uses = rather than >=.
The use of now() and >= is a bit confusing, because now() has a time component, which you generally want to ignore.

SQL to get number count per minute

I have a dataset like:
id name updated_at
1 test1 2014-06-30 09:00:00
1 test2 2014-06-30 09:01:10
1 test3 2014-06-30 09:01:23
1 test4 2014-06-30 09:01:43
1 test5 2014-06-30 09:02:02
1 test6 2014-06-30 09:02:34
1 test7 2014-06-30 09:03:22
1 test8 2014-06-30 09:03:28
I need to get a count of the rows by minute for the last ten minutes. So it should always return ten numbers being the count of the rows that were updated last. Any ideas on how to do it and efficiently?
Last 10 Results
http://sqlfiddle.com/#!9/3d586/22
--get the minute component of the update time
select minute(updated_at) as Sec
--count the number of records which have this minute
, count(1) as Cnt
from myTable
--use group by to ensure we return 1 row per minute
group by minute(updated_at)
--list from most recent working backwards
order by minute(updated_at) desc
--return up to 10 results
limit 10
Results for last 10 minutes
http://sqlfiddle.com/#!9/3d586/26
--get the minute component of the update time
select minute(y.d) as Min
--count the number of records which have this minute
--use m.id instead of 1 or * to ensure where there's no result from myTable
--we don't count any rows
, count(m.id) as Cnt
from
(
--get the current date's minute, offset by a given amount
select date_add(now(), interval -x.a minute) d
from
(
--the list of given amounts by which to offset the above date
select 0 a
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
) x
) y
--left join to ensure the above list drives which results we get,
--regardless of whether there are matching entries in myTable
left outer join myTable m
--join on the minute of each date
on minute(m.updated_at) = minute(y.d)
--use group by to ensure we return 1 row per minute
group by minute(y.d)
--list from most recent working backwards
order by minute(y.d) desc
Since you want interpolated values, it quickly becomes more complicated. It's probably easiest just to create a derived table with every minute from the last ten minutes as a row, then left join to that. Here's one way to do it:
select minute(now() - interval m minute) 'minutes ago', count(case when t.updated_at is not null then t.updated_at end)
from
(select minute(now()) m
union
select minute(now() - interval 1 minute) m
union
select minute(now() - interval 2 minute) m
union
select minute(now() - interval 3 minute) m
union
select minute(now() - interval 4 minute) m
union
select minute(now() - interval 5 minute) m
union
select minute(now() - interval 6 minute) m
union
select minute(now() - interval 7 minute) m
union
select minute(now() - interval 8 minute) m
union
select minute(now() - interval 9 minute) m
union
select minute(now() - interval 10 minute) m
) q
left join myTable t
on q.m = minute(t.updated_at)
and t.updated_at >= now() - interval 10 minute
group by m
order by m desc
There's a fiddle here but you'll have to alter the schema (just add a space somewhere) and rebuild it in order to get accurate results:
http://sqlfiddle.com/#!9/f3320/1

Get Date with Day of Week, Week Number, Month and Year

There some kind to get the Date if you have only the Day of Week, Week number, Month and Year with MySQL?
Example:
I Want to know which day is with this parameters:
Year : 2014
Month : Setember (09)
Week number of Year : 37 OR Week number in Setember : 3
Day of Week: Thursday
The Answer is '2014-09-18'
Using Barmars suggestion you can build a calendar of the year on the fly and check it against your constraints about that:
SET #year := 2014; -- set the year of the constraints
SET #week := 37; -- the week
SET #day_of_week := 5; -- the day of the week (numerical)
-- build the first of the wanted year as supposed by Barmar
SET #first_of_year = STR_TO_DATE(CONCAT(#year, '-01-01'), '%Y-%m-%d');
SELECT
#first_of_year + INTERVAL t.n DAY the_date
FROM (
SELECT
a.N + b.N * 10 + c.N * 100 AS n
FROM
(SELECT 0 AS 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) a
,(SELECT 0 AS 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) b
,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3) c
ORDER BY n
) t
WHERE
t.n < TIMESTAMPDIFF(DAY, #first_of_year, #first_of_year + INTERVAL 1 YEAR)
AND
WEEK(#first_of_year + INTERVAL t.n DAY) = #week
AND
DAYOFWEEK(#first_of_year + INTERVAL t.n DAY) = #day_of_week
;
Demo
Note
The UNION generates the numbers from 0 to 399, so we can generate a calendar of the year. Now we can apply your other constraints like week in year and day of week.
I asked the same question in portuguese Stake Overflow, and they found a simple solution.
Using str_to_date, year, week number and day of week.
%Y Year, numeric, four digits
%U Week (00..53), where Sunday is the first day of the week
%W Weekday name (Sunday..Saturday)
SELECT str_to_date('201437 Thursday', '%Y%U %W');
Result:
2014-09-18 00:00:00
Portuguese Stack Overflow Answer Link : https://pt.stackoverflow.com/questions/33046/obter-data-com-dia-da-semana-n%C3%BAmero-da-semana-m%C3%AAs-e-ano/33063#33063
Thanks to everyone who helped me