I am looking to breakdown a Month into weeks(starting Monday) in MySQL.
For example,
Sept 3 - 9th
Sept 10 - 16th
How to achieve this using MySql?
Thanks in advance.
This query will produce the results you want. It generates a table of possible week numbers in the month (0 to 4) using a UNION, and then it adds those week numbers to the computation of the first Monday of the month (stored in the variable #firstday which is JOINed to the table of week numbers).
SELECT #firstday + INTERVAL w WEEK AS start, #firstday + INTERVAL w WEEK + INTERVAL 6 DAY AS end
FROM (SELECT 0 AS w UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) weeks
JOIN (SELECT #firstday := FROM_DAYS(TO_DAYS(CURDATE())-DAY(CURDATE())+1) +
(7 - WEEKDAY(FROM_DAYS(TO_DAYS(CURDATE())-DAY(CURDATE())+1))) % 7) f
HAVING end <= LAST_DAY(#firstday)
Output:
start end
2018-09-03 2018-09-09
2018-09-10 2018-09-16
2018-09-17 2018-09-23
2018-09-24 2018-09-30
To run the query for any given month, replace CURDATE() in the computation of #firstday (4 places) with a date in the month you are interested in e.g.
SELECT #firstday + INTERVAL w WEEK AS start, #firstday + INTERVAL w WEEK + INTERVAL 6 DAY AS end
FROM (SELECT 0 AS w UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) weeks
JOIN (SELECT #firstday := FROM_DAYS(TO_DAYS('2018-01-20')-DAY('2018-01-20')+1) +
(7 - WEEKDAY(FROM_DAYS(TO_DAYS('2018-01-20')-DAY('2018-01-20')+1))) % 7) f
HAVING end <= LAST_DAY(#firstday)
Output:
start end
2018-01-01 2018-01-07
2018-01-08 2018-01-14
2018-01-15 2018-01-21
2018-01-22 2018-01-28
If you have the flexibility of setting variables, you can clean up the query like so:
SET #day = '2018-01-20';
SET #firstday := FROM_DAYS(TO_DAYS(#day)-DAY(#day)+1) + (7 - WEEKDAY(FROM_DAYS(TO_DAYS(#day)-DAY(#day)+1))) % 7;
SELECT #firstday + INTERVAL w WEEK AS start, #firstday + INTERVAL w WEEK + INTERVAL 6 DAY AS end
FROM (SELECT 0 AS w UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) weeks
HAVING end <= LAST_DAY(#firstday)
You can use function week(date) - https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_week
Related
I am trying to split a specific date range into chunks of 3 days to find the number of records per each three day chunks.
For example, assume I have this table:
User
Minimum Date
Maximum Date
Consecutive Days
1
09/20/2021
09/29/2021
10
And I want to produce this table:
User
Minimum Date
Maximum Date
1
09/20/2021
09/22/2021
1
09/23/2021
09/25/2021
1
09/26/2021
09/28/2021
The reason I ended it off there is because the remaining days are not enough to make up 3 days.
You need in something like
WITH RECURSIVE
cte AS (
SELECT User,
MinimumDate,
MinimumDate + INTERVAL 2 DAY MaximumDate, -- N-1
MaximumDate FinalDate
FROM sourcetable
WHERE MinimumDate + INTERVAL 2 DAY <= MaximumDate -- N-1
UNION ALL
SELECT User,
MinimumDate + INTERVAL 3 DAY, -- N
MinimumDate + INTERVAL 5 DAY, -- 2*N-1
FinalDate
FROM cte
WHERE MinimumDate + INTERVAL 5 DAY <= FinalDate -- 2*N-1
)
SELECT User,
MinimumDate,
MaximumDate
FROM cte
ORDER BY 1, 2;
or (not tested)
WITH RECURSIVE
cte1 AS ( SELECT (MAX(DATEDIFF(MaximumDate, MinimumDate)) DIV 3) - 1 maxrange
FROM sourcetable ),
cte2 AS ( SELECT 0 num
UNION ALL
SELECT num + 1 FROM cte2 WHERE num < maxrange )
SELECT User,
MinimumDate + INTERVAL 3 * num DAY MinimumDate,
MinimumDate + INTERVAL 2 + 3 * num DAY MaximumDate
FROM sourcetable
JOIN cte2 ON MinimumDate + INTERVAL 3 * num DAY <= MaximumDate
ORDER by 1,2
I need a table of 2 columns and 12 rows as a result of MySQL/SQL query:
First column is 12 months of the current year.
Second column is the number of days in each month.
Table should look similar to:
months days
January 31
February 28
... ...
December 31
I tried:
DELIMITER $$
DROP PROCEDURE IF EXISTS my_loop$$
CREATE PROCEDURE my_loop()
BEGIN
DECLARE x INT;
SET x = 1;
WHILE x <= 12 DO
SELECT monthname(SUBDATE("2019-12-01", INTERVAL x month)) as months, day(LAST_DAY(SUBDATE("2019-12-01", INTERVAL x month))) as days;
SET x = x + 1;
END WHILE;
END$$
DELIMITER ;
call my_loop();
But it prints out only the first iteration:
months days
January 31
How can I solve this with or without an iteration?
You can do this with a query:
select monthname(makedate(year(curdate()), 1) + interval (x.mon - 1) month) as month_name,
day(last_day(makedate(year(curdate()), 1) + interval (x.mon - 1) month)) as days_in_month
from (select 1 as mon union all select 2 union all select 3 union all select 4 union all
select 5 as mon union all select 6 union all select 7 union all select 8 union all
select 9 as mon union all select 10 union all select 11 union all select 12
) x
order by x.mon;
Here is a db<>fiddle.
I'm new to stored procedures so I may be doing this wrong. I've created a stored procedure that brings back month name, month start and month end but I'm getting 12 separate datasets.
How can I combine this into one?
BEGIN
SET #i = 0;
SET #start_date = "2018-09-01";
SET #end_date = LAST_DAY('2018-09-01');
WHILE #i < 12 DO
SELECT MONTHNAME(#start_date) AS month, #start_date, #end_date;
SET #i = #i + 1;
SET #start_date = DATE_ADD(#start_date, INTERVAL 1 MONTH);
SET #end_date = LAST_DAY(DATE_ADD(#end_date, INTERVAL 1 MONTH));
END WHILE;
END
You don't need a stored procedure for that.
set #start_date = '2018-09-01';
with recursive months(m) as (
select 0
union all
select m + 1
from months
where m < 11
)
select #start_date + interval m.m month as first_day
, last_day(#start_date + interval m.m month) as last_day
from months m
order by m.m asc
Result:
first_day last_day
2018-09-01 2018-09-30
2018-10-01 2018-10-31
2018-11-01 2018-11-30
2018-12-01 2018-12-31
2019-01-01 2019-01-31
2019-02-01 2019-02-28
2019-03-01 2019-03-31
2019-04-01 2019-04-30
2019-05-01 2019-05-31
2019-06-01 2019-06-30
2019-07-01 2019-07-31
2019-08-01 2019-08-31
But you can ofcourse use that query in your SP too.
Rewrote #Paul Spiegel query so that I can understand how recursive queries work.
SET #start = "2018-09-01";
WITH RECURSIVE months (m)
AS (
SELECT 0
UNION ALL
SELECT m + 1
FROM months
WHERE m < 12
)
SELECT
MONTHNAME(DATE_ADD(#start, INTERVAL m MONTH)) AS month_name,
CONCAT(DATE_ADD(#start, INTERVAL m MONTH), " 00:00:00") AS start_of_month,
CONCAT(LAST_DAY(DATE_ADD(#start, INTERVAL m MONTH)), " 23:59:59") AS end_of_month
FROM months;
For MySQL versions before 8.0 , we can do something like this:
SELECT MONTHNAME(d.dt + INTERVAL i.n MONTH) AS month
, d.dt + INTERVAL i.n MONTH AS start_date
, LAST_DAY(d.dt + INTERVAL i.n MONTH) AS end_date
FROM ( SELECT '2018-09-01' + INTERVAL 0 MONTH AS dt ) d
CROSS
JOIN ( 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
) i
ORDER
BY i.n
I want to get the three working days from the current date as excluding Saturday and Sunday. can any one help me out here.
I have tried the interval method and DayOfWeek(day) <> 1 AND DayOfWeek(day) <> 7 but it is not giving me the proper result
Not very elegant but
select d
from
(
select curdate() as d
union all
select curdate() + interval 1 day
union all
select curdate() + interval 2 day
union all
select curdate() + interval 3 day
union all
select curdate() + interval 4 day
) tmp
where dayofweek(d) not in (1,7)
order by d
limit 3
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