How to get this coming Sunday's date? - mysql

I need to get "this coming Sunday"'s date
How could I do this?
I've seen the DAYOFWEEK function but that's not really what I'm after (might be useful in the WHERE clause)
EDIT:
I changed question back from my change to Saturday to the original Sunday since I got some valid answers for Sunday and it may help some people in the future
I found a number of other 'end of week' date questions and answers elsewhere (including here on SO)
The most useful solution for me was the accepted answer found here.

UPDATE
Better use curdate() instead of now(), so the final conversion to DATE type via date() can be omitted.
SELECT curdate() + INTERVAL 6 - weekday(curdate()) DAY;
for next sunday or
SET #date = '2014-03-05';
SELECT #date + INTERVAL 6 - weekday(#date) DAY;
for a variable date.
ORIGINAL ANSWER
Simply use this statement for the next sunday:
SELECT date(now() + INTERVAL 6 - weekday(now()) DAY);
Output
+-----------------------------------------------+
| date(now() + INTERVAL 6 - weekday(now()) DAY) |
+-----------------------------------------------+
| 2014-04-13 |
+-----------------------------------------------+
Explanation:
weekday(now()) returns the current weekday (starting with 0 for monday, 6 is sunday). Subtract the current weekday from 6 and get the remaining days until next sunday as a result. Then add them to the current date and get next sunday's date.
Or if you want to keep it flexible to work with any date:
SET #date = '2014-03-05';
SELECT date(#date + INTERVAL 6 - weekday(#date) DAY);
Output
+-----------------------------------------------+
| date(#date + INTERVAL 6 - weekday(#date) DAY) |
+-----------------------------------------------+
| 2014-03-09 |
+-----------------------------------------------+

To find the next date for given week day use this query. set the value of the week day you want in the THEDAY variable.
SET #THEDAY = 1;
SELECT DATE_ADD(NOW(), INTERVAL if( #THEDAY > WEEKDAY(NOW()) , (#THEDAY - WEEKDAY(NOW())) , (7 - WEEKDAY(NOW())) + #THEDAY) DAY)
Note that the weekday index starts from 0 For Monday ...... 6 For Sunday

Date()+8-WeekDay(Date())
and try this
=IIf(Weekday(Now())=1,DateAdd("d",7,Now()),
IIf(Weekday(Now())=2,DateAdd("d",6,Now()),
IIf(Weekday(Now())=3,DateAdd("d",5,Now()),
IIf(Weekday(Now())=4,DateAdd("d",4,Now()),
IIf(Weekday(Now())=5,DateAdd("d",3,Now()),
IIf(Weekday(Now())=6,DateAdd("d",2,Now()),
IIf(Weekday(Now())=6,DateAdd("d",1,Now()) )))))))

the WEEK function can be paired with STR_TO_DATE for a nice one-liner:
SELECT STR_TO_DATE(CONCAT(YEAR(NOW()),WEEK(NOW(),1), ' Sunday'), '%X%V %W') as `this Sunday`;
+-------------+
| this Sunday |
+-------------+
| 2014-04-13 |
+-------------+;
If you follow the doc links, the overall strategy is mostly gleaned from the note on STR_TO_DATE about year/week conversion. Then the mode argument to WEEK is used to treat Sunday as the last instead of first day of the week. Essentially, we construct a string like this:
'201415 Sunday'
Which is to be interpreted as "in 2014, Sunday of the 15th week".
Just in case, I tested this at the year boundary... looks okay so far!
SELECT STR_TO_DATE(CONCAT(YEAR('2014-12-31'),WEEK('2014-12-31',1), ' Sunday'), '%X%V %W') as `this Sunday`;
+-------------+
| this Sunday |
+-------------+
| 2015-01-04 |
+-------------+

I posted a similar answer in What is the MySQL alternative to Oracle's NEXT_DAY function? which a more general solution to the question being asked here. The solution below will help you get to any specified next day (Sunday, Monday, Tuesday, Wednesday, Thursday, Friday or Saturday) similar to the NEXT_DAY() function in oracle.
Short Version:
Utilizing DAYOFWEEK() you can leverage the number values to create a closed formula that tells you how many days to add. You can then utilize DATE_ADD to help you shift the date. There may be a cleaner way, however you should be able to make a function out of this.
The following is an Excel formula, which you use to verify all 49 use cases: =MOD(MOD(Next-Input, 7)+7,7)
Some examples of how the excel calculations can be translated into SQL:
SELECT (((1-DAYOFWEEK('2018-08-15')) % 7)+7) % 7 AS DaysToAddToGetNextSundayFromWednesday
FROM dual; -- 4
SELECT DATE_ADD('2018-08-15', INTERVAL (((1-DAYOFWEEK('2018-08-15')) % 7)+7) % 7 DAY) AS NextSundayFromDate
FROM dual; -- 2018-08-19
If you plan to use the above often, you'll probably want to make a function or stored procedure.
Long Version:
I've run into this problem multiple times over the years. First time I created a giant case statement for all 49 cases. I found that this made the queries super big and not clean. I wanted a cleaner, simpler solution like a closed in formula. Below are details to to calculations I did to confirm the formula above works. I tested this with MySQL 5.7 if you are using MySQL 8.5 it looks like the function is built in ( Reference: http://www.sqlines.com/mysql/how-to/next_day ).
Note: Excel doesn't return negative results for modulus, where MySQL does. That is why we add 7 and do another modulus.
DAYOFWEEK()
Sun 1
Mon 2
Tues 3
Wed 4
Thur 5
Fri 6
Sat 7
Input Next Expected Days to Add Formula Result
1 1 0 0
1 2 1 1
1 3 2 2
1 4 3 3
1 5 4 4
1 6 5 5
1 7 6 6
2 1 6 6
2 2 0 0
2 3 1 1
2 4 2 2
2 5 3 3
2 6 4 4
2 7 5 5
3 1 5 5
3 2 6 6
3 3 0 0
3 4 1 1
3 5 2 2
3 6 3 3
3 7 4 4
4 1 4 4
4 2 5 5
4 3 6 6
4 4 0 0
4 5 1 1
4 6 2 2
4 7 3 3
5 1 3 3
5 2 4 4
5 3 5 5
5 4 6 6
5 5 0 0
5 6 1 1
5 7 2 2
6 1 2 2
6 2 3 3
6 3 4 4
6 4 5 5
6 5 6 6
6 6 0 0
6 7 1 1
7 1 1 1
7 2 2 2
7 3 3 3
7 4 4 4
7 5 5 5
7 6 6 6
7 7 0 0

The accepted answer will return previous Saturday on Sunday.
If you need the next Saturday for Sunday use this
SELECT
CAST(
Case When weekday(now()) = 6 Then (now() + INTERVAL weekday(now()) DAY)
else (now() + INTERVAL 5 - weekday(now()) DAY)
end
As Date) as saturday

Related

Change Data Capture Redshift

I have a table
DAY 1
ID
amount
DATE
1
10
12-02-2020
2
15
12-02-2020
3
20
12-02-2020
4
25
12-02-2020
I did a sum of the amount on day one which turns out to be 70
Now next day I have few more rows where the amount is UPDATED an APPENDED
New tables looks like this
DAY 2
ID
amount
DATE
1
10
12-02-2020
2
20
13-02-2020
3
20
12-02-2020
4
25
12-02-2020
5
30
13-02-2020
6
35
14-02-2020
Now if you see the ID 2 has new updates amount which is 20 earlier 15
and it has new data from dates 13 and 14 on ID 5 and 6
Can I just run a query where it will only process the changed data and add it to the
previous sum
so like 30+35+5(as only 5 increased from the last value)
total = 70
Mainly to process changed data
This will very much depend on how the historical data will be provided.
This example requires additional Day column in the historical data table AND that you're using a MySQL version that supports LAG() (e.g. MySQL v8+ OR MariaDB 10.3+). Let's assume that it's possible for the historical data table to be like this:
ID
Amount
Date
Day
1
10
2020-02-12
1
2
15
2020-02-12
1
3
20
2020-02-12
1
4
25
2020-02-12
1
1
10
2020-02-12
2
2
20
2020-02-13
2
3
20
2020-02-12
2
4
25
2020-02-12
2
5
30
2020-02-13
2
6
35
2020-02-14
2
.. then maybe a query like this:
SELECT Day,
SUM(amount) AS Total,
SUM(amount)-LAG(SUM(amount)) OVER (ORDER BY Day) AS diff
FROM historical_data
GROUP BY Day
ORDER BY Day;
OR (in for MariaDB):
SELECT Day, Total,
Total-LAG(Total) OVER (ORDER BY Day) AS Diff
FROM
(SELECT Day,
SUM(amount) AS Total
FROM historical_data
GROUP BY Day) A;
This will return result like:
Day
Total
diff
1
70
2
140
70
I was following an example from this site on how to use LAG() to get the row data value above it an using them to subtract the SUM(amount) value for that day.
Here's a demo fiddle of the experiment.

Sort dbReadtable using wday in R

I have a dbReadtable which I wanna sort by week date. My table looks as follow:
Id Date Number
1 2020-11-01 1
2 2020-11-03 3
3 2020-11-02 4
4 2020-11-01 2
5 2020-11-02 4
6 2020-11-03 3
7 2020-11-06 7
8 2020-11-05 4
9 2020-11-08 2
I want the output to look something like this:
Day Number
Sunday 5
Monday 8
Tuseday 6
Thursday 4
Friday 7
For this, I wanna use $wday in lubridate library.
My code looks as follow:
data <- dbReadTable(con, "observations")
How do I solve this?
Using data.table and lubridate and assuming Date is already a date (not a string).
library(lubridate)
library(data.table)
data <- dbReadTable(con, "observations")
data[, by=.(Day=wday(Date, label=TRUE, abbr=FALSE, locale="C")), .(Number=sum(Number))]
# Day Number
#1: Sunday 5
#2: Tuesday 6
#3: Monday 8
#4: Friday 7
#5: Thursday 4
I added locale to return english names for days (I'm french and get french names :-).

What is the MySQL alternative to Oracle's NEXT_DAY function?

NEXT_DAY("01-SEP-95","FRIDAY") returns what is the date on next Friday, but in MySQL this function does not seem to be appear. What is the alternative?
I'm going to throw my hat in the ring with yet another approach:
Edit: I realize somewhat belatedly that the Oracle function in question takes a string as the second argument, and so this doesn't precisely fit the requirement. However MySQL has already kindly defined 0 - 6 as Monday - Sunday, and anyway I have moral objections to using a string as an argument for this type of thing. A string would either come from user input or yet another mapping in higher level code between numeric and string values. Why not pass an integer? :)
CREATE FUNCTION `fnDayOfWeekGetNext`(
p_date DATE,
p_weekday TINYINT(3)
) RETURNS date
BEGIN
RETURN DATE_ADD(p_date, INTERVAL p_weekday - WEEKDAY(p_date) + (ROUND(WEEKDAY(p_date) / (p_weekday + WEEKDAY(p_date) + 1)) * 7) DAY);
END
To break the portion down that determines the INTERVAL value:
The first part of the equation simply gets the offset between the weekday specified and the weekday of the date specified:
p_weekday - WEEKDAY(p_date)
This will return a positive number if p_weekday is greater than WEEKDAY(p_date) and vice versa. Zero will be returned if they're the same.
The ROUND() segment is used to determine whether the requested day of the week (p_weekday) has already occurred in the current week relative to the date (p_date) specified. So, by example...
ROUND(WEEKDAY('2019-01-25') / (6 + WEEKDAY('2019-01-25') + 1))
..returns 0, indicating that Sunday (6) has not occurred this week, as 2019-01-25 is a Friday. Likewise...
ROUND(WEEKDAY('2019-01-25') / (2 + WEEKDAY('2019-01-25') + 1))
...returns 1 because Wednesday (2) has already passed. Note that this will return 0 if p_weekday is the same as the weekday of p_date.
This value (either 1 or 0) is then multiplied by the constant 7 (the number of days in a week).
Hence if p_weekday has already occurred in the current week, it will add 7 to the offset p_weekday - WEEKDAY(p_date), because that offset would be a negative number and we want a date in the future.
If p_weekday has yet to occur in the current week, then we can just add the offset to the current date because the offset will be a positive number. Hence the section ROUND(...) * 7 is equal to zero and, in essence, ignored.
My desire for this approach was to simulate an IF() condition mathematically. This would be equally valid:
RETURN DATE_ADD(p_date, INTERVAL p_weekday - WEEKDAY(p_date) + IF(p_weekday - WEEKDAY(p_date) < 0, 7, 0) DAY);
And in the interest of objectivity, in running 1M iterations a few times of each function the IF-based version averaged about 4.2% faster than the ROUND-based version.
In MySQL, you can create user defined function
DELIMITER //
 
CREATE FUNCTION next_day(start_date DATETIME, weekday CHAR(20))
RETURNS DATETIME
BEGIN
DECLARE start DATETIME;
DECLARE i INT;
 
// Select the next date
SET start = ADDDATE(start_date, 1);
SET i = 1;
 
days: LOOP
-- Compare the day names
IF SUBSTR(DAYNAME(start), 1, 3) = SUBSTR(weekday, 1, 3) THEN
LEAVE days;
END IF;
 
// Select the next date
SET start = ADDDATE(start, 1);
SET i = i + 1;
 
-- Not valid weekday specified
IF i > 7 THEN
SET start = NULL;
LEAVE days;
END IF;
 
END LOOP days;
 
RETURN start;
END;
//
 
DELIMITER ;
and call it
SELECT NEXT_DAY("1995-09-01","FRIDAY")
Source : http://www.sqlines.com/mysql/how-to/next_day
Here's an answer to a different problem:
Find the date for Friday of the current week:-
SELECT STR_TO_DATE(CONCAT('2017',(SELECT DATE_FORMAT(CURDATE(),'%U')),' Friday'),'%Y %U %W') x;
+------------+
| x |
+------------+
| 2017-05-12 |
+------------+
The above hasn't been verified for robustness, but is provided 'as is' merely as food for thought.
SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE your_table ( value DATE );
INSERT INTO your_table ( value )
SELECT DATE '2017-05-08' FROM DUAL UNION ALL
SELECT DATE '2017-05-09' FROM DUAL UNION ALL
SELECT DATE '2017-05-10' FROM DUAL UNION ALL
SELECT DATE '2017-05-11' FROM DUAL UNION ALL
SELECT DATE '2017-05-12' FROM DUAL UNION ALL
SELECT DATE '2017-05-13' FROM DUAL UNION ALL
SELECT DATE '2017-05-14' FROM DUAL;
Query 1:
SELECT value,
ADDDATE(
value,
6 - DAYOFWEEK( value )
+ CASE WHEN DAYOFWEEK( value ) < 6 THEN 0 ELSE 7 END
) AS next_friday
FROM your_table
Results:
| value | next_friday |
|-----------------------|-----------------------|
| May, 08 2017 00:00:00 | May, 12 2017 00:00:00 |
| May, 09 2017 00:00:00 | May, 12 2017 00:00:00 |
| May, 10 2017 00:00:00 | May, 12 2017 00:00:00 |
| May, 11 2017 00:00:00 | May, 12 2017 00:00:00 |
| May, 12 2017 00:00:00 | May, 19 2017 00:00:00 |
| May, 13 2017 00:00:00 | May, 19 2017 00:00:00 |
| May, 14 2017 00:00:00 | May, 19 2017 00:00:00 |
Short Version:
Utilizing DAYOFWEEK() you can leverage the number values to create a closed formula that tells you how many days to add. You can then utilize DATE_ADD to help you shift the date. There may be a cleaner way, however you should be able to make a function out of this.
The following is an Excel formula, which you use to verify all 49 use cases: =MOD(MOD(Next-Input, 7)+7,7)
Some examples of how the excel calculations can be translated into SQL:
SELECT (((1-DAYOFWEEK('2018-08-15')) % 7)+7) % 7 AS DaysToAddToGetNextSundayFromWednesday
FROM dual; -- 4
SELECT DATE_ADD('2018-08-15', INTERVAL (((1-DAYOFWEEK('2018-08-15')) % 7)+7) % 7 DAY) AS NextSundayFromDate
FROM dual; -- 2018-08-19
If you plan to use the above often, you'll probably want to make a function or stored procedure.
Long Version:
I've run into this problem multiple times over the years. First time I created a giant case statement for all 49 cases. I found that this made the queries super big and not clean. I wanted a cleaner, simpler solution like a closed in formula. Below are details to to calculations I did to confirm the formula above works. I tested this with MySQL 5.7 if you are using MySQL 8.5 it looks like the function is built in ( Reference: http://www.sqlines.com/mysql/how-to/next_day ).
Note: Excel doesn't return negative results for modulus, where MySQL does. That is why we add 7 and do another modulus.
DAYOFWEEK()
Sun 1
Mon 2
Tues 3
Wed 4
Thur 5
Fri 6
Sat 7
Input Next Expected Days to Add Formula Result
1 1 0 0
1 2 1 1
1 3 2 2
1 4 3 3
1 5 4 4
1 6 5 5
1 7 6 6
2 1 6 6
2 2 0 0
2 3 1 1
2 4 2 2
2 5 3 3
2 6 4 4
2 7 5 5
3 1 5 5
3 2 6 6
3 3 0 0
3 4 1 1
3 5 2 2
3 6 3 3
3 7 4 4
4 1 4 4
4 2 5 5
4 3 6 6
4 4 0 0
4 5 1 1
4 6 2 2
4 7 3 3
5 1 3 3
5 2 4 4
5 3 5 5
5 4 6 6
5 5 0 0
5 6 1 1
5 7 2 2
6 1 2 2
6 2 3 3
6 3 4 4
6 4 5 5
6 5 6 6
6 6 0 0
6 7 1 1
7 1 1 1
7 2 2 2
7 3 3 3
7 4 4 4
7 5 5 5
7 6 6 6
7 7 0 0

schedule after 0:00 hour not working before first interval

I have a table with 4 schedule interval:
id time_int progr_comment A B C D
1 05:30:00 Good Morning 1 4 2 17
2 06:50:00 Have a nice day 1 4 2 17
3 17:00:00 Welcome Home 1 4 4 23
4 18:30:00 Good Evening 1 4 2 22
5 22:00:00 Good Night 1 4 2 20
For each interval I compare with NOW() and I will take the variables (A, B, C, D) from table based on time_int <= NOW() then my program create a query to mysql. The problem come after 0:00 hour. The query does not find anything until NOW() pass the first interval (ex. 5:30:00).

Sum of overlap durations for each combination of 2 foreign keys in MySQL

i have a database with workers, stations and session. A session describes at which time which worker has been on which station. I managed to build a query that gives me the duration of the overlap of each session.
SELECT
sA.station_id,
sA.worker_id AS worker1,
sB.worker_id AS worker2,
SEC_TO_TIME(
TIME_TO_SEC(LEAST(sA.end,sB.end)) - TIME_TO_SEC(GREATEST(sA.start,sB.start))
) AS overlap
FROM
`sessions` AS sA,
`sessions` AS sB
WHERE
sA.station_id = sb.station_id
AND
sA.station_id = 6
AND (
sA.start BETWEEN sB.start AND sB.end
OR
sA.end BETWEEN sB.start AND sB.end
)
With this query i get an result like this
station_id worker1 worker2 overlap
6 1 1 09:00:00
6 2 1 02:30:00
6 5 1 00:00:00
6 1 1 09:00:00
6 2 1 01:30:00
6 3 1 09:00:00
...
6 12 3 02:00:00
6 14 3 01:00:00
6 17 3 02:00:00
...
What i would like now is to sum up the overlap for every combination of worker1 and worker2 to get the overall overlap duration.
I tried different ways of using SUM() and GROUP BY but i never got the wanted result.
SELECT
...
SEC_TO_TIME(
**SUM**(TIME_TO_SEC(LEAST(sA.end,sB.end)) - TIME_TO_SEC(GREATEST(sA.start,sB.start)))
) AS overlap
...
#has as result
station_id worker1 worker2 overlap
6 1 1 838:59:59
#in combination with
GROUP BY
worker1
#i get
station_id worker1 worker2 overlap
6 1 1 532:30:00
6 2 1 -33:00:00
6 3 1 270:30:00
6 5 1 598:30:00
6 6 1 542:00:00
6 7 1 508:00:00
6 8 5 53:00:00
6 9 1 54:30:00
6 10 1 310:00:00
6 11 1 -108:00:00
6 12 1 593:30:00
6 14 1 97:30:00
6 15 1 -53:30:00
6 17 1 293:30:00
the last result is close but i am still missing a lot of combinations. I also dont understand why the combination 8 - 5 is displayed.
thanks for ur help (and time to read)
aaargh, sorry for my stupidity, the solution was fairly simple
....
SUM(((UNIX_TIMESTAMP(LEAST(sA.end,sB.end))-UNIX_TIMESTAMP(GREATEST(sA.start,sB.start)))/3600))
...
GROUP BY station_id, worker1, worker2
ORDER BY worker1, worker2
i switched to using timestamps and transforming it to hours by /3600 because my former used approach with TIME_TO_SEC and SEC_TO_TIME only used the TIME part of the DATETIME field and thereby produced some wrong numbers. With MySQL 5.5 i could use TO_SECONDS but unfortunately my server is still runing 5.1.