select dates on this weekday in the past - mysql

I have a list of dates ("start", datetime) and I would like to select all dates where :
today = start + 1 WEEK or
today = start + 2 WEEK or
today = start + 3 WEEK or
today = start + 4 WEEK or
today = start + 5 WEEK or
today = start + 6 WEEK
Maximum is start + 6 weeks.
Any idea ?

setup
create table example
(
id integer primary key not null auto_increment,
start datetime not null
);
insert into example ( start )
values
( date_sub(current_date, interval 1 week) ),
( date_sub(current_date, interval 2 week) ),
( date_sub(current_date, interval 3 week) ),
( date_sub(current_date, interval 4 week) ),
( date_sub(current_date, interval 5 week) ),
( date_sub(current_date, interval 6 week) ),
( date_sub(current_date, interval 6 week) ),
( date_sub(current_date, interval 4 week) ),
( date_sub(current_date, interval 9 week) ),
( date_sub(current_date, interval 12 week) )
;
query
select id, start
from example
where
date(start) in
(
date_sub(current_date, interval 1 week) ,
date_sub(current_date, interval 2 week) ,
date_sub(current_date, interval 3 week) ,
date_sub(current_date, interval 4 week) ,
date_sub(current_date, interval 5 week) ,
date_sub(current_date, interval 6 week)
)
;
output
+----+-----------------------------+
| id | start |
+----+-----------------------------+
| 1 | September, 16 2015 00:00:00 |
| 2 | September, 09 2015 00:00:00 |
| 3 | September, 02 2015 00:00:00 |
| 4 | August, 26 2015 00:00:00 |
| 5 | August, 19 2015 00:00:00 |
| 6 | August, 12 2015 00:00:00 |
| 7 | August, 12 2015 00:00:00 |
| 8 | August, 26 2015 00:00:00 |
+----+-----------------------------+
sqlfiddle

I assume you want a WHERE filter to capture all rows containing start DATETIMEs on this weekday one week ago, and two ... six weeks ago. That's the effect of the logic in your question:
today = start + 1 WEEK or today = start + 2 WEEK or ...
means the same thing as
start = today - 1 WEEK etc.
The thing is, you are using DATETIME values for start. They're not guaranteed to be start = CURDATE() because they may not be at midnight.
So, you need to use the DATE() function to reduce them to midnight values before comparing them. Something like this will work.
WHERE DATE(start) IN (
CURDATE() - INTERVAL 6 WEEK, CURDATE() - INTERVAL 5 WEEK, CURDATE() - INTERVAL 4 WEEK,
CURDATE() - INTERVAL 3 WEEK, CURDATE() - INTERVAL 2 WEEK, CURDATE() - INTERVAL 1 WEEK)
You could also do this -- it picks out all records six weeks old or newer, but not the ones in the most recent week, then picks the ones on today's weekday.
WHERE start >= CURDATE() - INTERVAL 6 WEEK
AND start < CURDATE() - 6 DAY
AND WEEKDAY(CURDATE()) = WEEKDAY(start)
This second formulation will be more efficient if you have a great deal of old data in your table and you have an index on your start column: the first two where clauses are sargeable.
Pro tip: When specifying this kind of date filter, the more effort you spend making your specification exact before you write code, the faster you will finish your work. That's true even if you don't count debugging time.

Could you use between?
So...
today BETWEEN start AND start + INTERVAL 6 WEEK
Something like that?

Try To run this query in mysql,
use dbname;
create table adddate
(
id integer primary key not null auto_increment,
initial datetime not null
)auto_increment=100;
insert into adddate ( initial )
values( DATE_ADD(current_date, interval 1 week) ),
( DATE_ADD(current_date, interval 2 week) ),
( DATE_ADD(current_date, interval 3 week) ),
( DATE_ADD(current_date, interval 4 week) ),
( DATE_ADD(current_date, interval 5 week) ),
( DATE_ADD(current_date, interval 6 week) )
;
date_add is an SQLfunction it is used to do addition operations on date
The syntax is:-
DATE_ADD(date,INTERVAL expr type)
the type can be:-
MICROSECOND,
SECOND,
MINUTE,
HOUR,
DAY,
WEEK,
MONTH,
QUARTER,
YEAR,
SECOND_MICROSECOND,
MINUTE_MICROSECOND,
MINUTE_SECOND,
HOUR_MICROSECOND,
HOUR_SECOND,
HOUR_MINUTE,
DAY_MICROSECOND,
DAY_SECOND,
DAY_MINUTE,
DAY_HOUR,
YEAR_MONTH;

Related

How to retrieve data from previous 4 weeks (mySQL)

I am trying to write a query to get the last 4 weeks (Mon-Sun) of data. I want every week of data to be stored with an individual and shared table.
every week data store based on name if same name repeated on single week amt should sum and if multiple name it should be show data individual, To see an example of what I am looking for, I have included the desired input and output below.
this is my table
date
amt
name
2022-04-29
5
a
2022-04-28
10
b
2022-04-25
11
a
2022-04-23
15
b
2022-04-21
20
b
2022-04-16
20
a
2022-04-11
10
a
2022-04-10
5
b
2022-04-05
5
b
i want output like this
date
sum(amt)
name
2022-04-25 to 2020-04-29
16
a
2022-04-25 to 2020-04-29
10
b
2022-04-18 to 2022-04-24
35
b
2022-04-11 to 2022-04-17
30
a
2022-04-04 to 2022-04-10
10
b
I would appreciate any pointers or 'best-practises' which I should employ to achieve this task.
You can try to use DATE_ADD with WEEKDAY get week first day and end day.
SELECT
CASE WHEN
weekofyear(`date`) = weekofyear(NOW())
THEN 'current week'
ELSE
CONCAT(date_format(DATE_ADD(`date`, interval - WEEKDAY(`date`) day), '%Y-%m-%d'),' to ',date_format(DATE_ADD(DATE_ADD(`date`, interval -WEEKDAY(`date`) day), interval 6 day), '%Y-%m-%d'))
END 'date',
SUM(amt)
FROM T
GROUP BY
CASE WHEN
weekofyear(`date`) = weekofyear(NOW())
THEN 'current week'
ELSE
CONCAT(date_format(DATE_ADD(`date`, interval - WEEKDAY(`date`) day), '%Y-%m-%d'),' to ',date_format(DATE_ADD(DATE_ADD(`date`, interval -WEEKDAY(`date`) day), interval 6 day), '%Y-%m-%d'))
END
sqlfiddle
EDIT
I saw you edit your question, you can just add name in group by
SELECT
CONCAT(date_format(DATE_ADD(`date`, interval - WEEKDAY(`date`) day), '%Y-%m-%d'),' to ',date_format(DATE_ADD(DATE_ADD(`date`, interval -WEEKDAY(`date`) day), interval 6 day), '%Y-%m-%d')) 'date',
SUM(amt),
name
FROM T
GROUP BY
CONCAT(date_format(DATE_ADD(`date`, interval - WEEKDAY(`date`) day), '%Y-%m-%d'),' to ',date_format(DATE_ADD(DATE_ADD(`date`, interval -WEEKDAY(`date`) day), interval 6 day), '%Y-%m-%d')),
name
ORDER BY 1 desc
sqlfiddle
This is in SQL Server, and just a mess about. Hopefully it can be of some help.
with cteWeekStarts
as
(
select
n,dateadd(week,-n,DATEADD(week, DATEDIFF(week, -1, getdate()), -1)) as START_DATE
from
(values (1),(2),(3),(4)) as t(n)
), cteStartDatesAndEndDates
as
(
select *,dateadd(day,-1,lead(c.start_date) over (order by c.n desc)) as END_DATE
from cteWeekStarts as c
)
,cteSalesSumByDate
as
(
select s.SalesDate,sum(s.salesvalue) as sum_amt from
tblSales as s
group by s.SalesDate
)
select c3.n as WeekNum,c3.START_DATE,isnull(c3.END_DATE,
dateadd(day,6,c3.start_date)) as END_DATE,
(select sum(c2.sum_amt) from cteSalesSumByDate as c2 where c2.SalesDate
between c3.START_DATE and c3.END_DATE) as AMT
from cteStartDatesAndEndDates as c3
order by c3.n desc

MySQL - Get Aggregates For Last 1 Day, 7 Days, 30 Days And Allow For Records To Be Counted In More Than 1 Group

I have a table with the following data:
I am looking to group the rows into the following:
Within the last day (everything within the last 24 hours)
Within the last 7 days (everything within the last week)
Within the last 30 days (everything within the last month)
The end result for the above rows would look something like:
I can group the records into these brackets right now with:
SELECT (CASE WHEN created_at = CURDATE() THEN '1 Day'
WHEN created_at >= CURDATE() - INTERVAL 6 DAY THEN '7 Days'
WHEN created_at >= CURDATE() - INTERVAL 29 DAY THEN '30 Days'
END) AS Timeframe, COUNT(*) AS Count
FROM my_table
GROUP BY (CASE WHEN created_at = CURDATE() THEN '1 Day'
WHEN created_at >= CURDATE() - INTERVAL 6 DAY THEN '7 Days'
WHEN created_at >= CURDATE() - INTERVAL 29 DAY THEN'30 Days'
END)
But this will prevent individual records from being counted more than once. For example, lines 2 and 3 in the first picture needs to be counted in all three brackets (1 day, 7 days, and 30 days) - while lines 6 through 9 only needs to be counted in the 30 days bracket.
How would you do this with MySQL?
It is easiest to do this as columns, rather than rows:
SELECT SUM(created_at = CURDATE()) as today
SUM(created_at >= CURDATE() - INTERVAL 6 DAY) as last_7_days,
SUM(created_at >= CURDATE() - INTERVAL 29 DAY) as last_30_days,
SUM(created_at < CURDATE() - INTERVAL 29 DAY) as older
FROM my_table;
If you want your response in several rows, instead of just one with several columns, take #Gordon Linoff as your starting point... but perform the queries "one row at at time" (it won't be as efficient, because you visit the table 4 times instead of 1!):
-- Row for the 1 day timeframe
SELECT '1 Day' AS `Timeframe`, SUM(created_at = CURDATE()) AS `Count`
FROM my_table
UNION
-- Row for the 7 days timeframe...
SELECT '7 Days' AS `Timeframe`, SUM(created_at >= CURDATE() - INTERVAL 6 DAY) AS `Count`
FROM my_table
UNION
SELECT '30 Days' AS `Timeframe`, SUM(created_at >= CURDATE() - INTERVAL 29 DAY) AS `Count`
FROM my_table
UNION
SELECT 'Older' AS `Timeframe`, SUM(created_at < CURDATE() - INTERVAL 29 DAY) AS `Count`
FROM my_table ;
If you can use MariaDB instead of MySQL, you can use a WITH, which will allow the query to be efficient again:
WITH stats AS
(
SELECT SUM(created_at = CURDATE()) as today,
SUM(created_at >= CURDATE() - INTERVAL 6 DAY) as last_7_days,
SUM(created_at >= CURDATE() - INTERVAL 29 DAY) as last_30_days,
SUM(created_at < CURDATE() - INTERVAL 29 DAY) as older
FROM my_table
)
-- Convert to rows with negligible overhead
SELECT '1 Day' AS `Timeframe`, today FROM stats
UNION
SELECT '7 Days', last_7_days FROM stats
UNION
SELECT '30 Days', last_30_days FROM stats
UNION
SELECT 'Older', older FROM stats ;
In both cases, you'll get (as of 2017-07-25):
Timeframe | today
:-------- | ----:
1 Day | 0
7 Days | 4
30 Days | 8
Older | 0
dbfiddle here

MySQL Where date between 9 and 10 days

I'm trying to make a query that will select all my users who's donor status is ending within 10 days.
As i only want to send the message once I want to select all the users who has their donor status ending between 10 and 9 days ahead.
The Donor end date is in this format: 0000-00-00 00:00:00
This is the query I'm currently working around with:
SELECT UserID FROM users_info WHERE donorEnd BETWEEN (NOW() + INTERVAL 10 DAY) AND (NOW() + INTERVAL 9 DAY)
I think you problem is that you are adding a time not a date: NOW() + INTERVAL 9 DAY = 2015-02-27 19:19 not 2015-02-27 00:00
Try use ADDDATE with CURDATE():
SELECT UserID FROM users_info WHERE donorEnd BETWEEN DATE_ADD(CURDATE(), INTERVAL 9 DAY) AND DATE_ADD(CURDATE(), INTERVAL 9 DAY)

Query data in an specified interval

I am trying to query a timesheet where time span (cycle) is 21 to 20, thus today (10/30/2014) is November.
So, 10/14/2014 is October.
2014-09-21 and 2014-10-20 is one time span
2014-10-21 and 2014-11-20 is one time span
The table is simply storing the date for the timesheet:
id date hours
So, I can specifically say:
SELECT * FROM rapoarte WHERE DATE(ziua) BETWEEN "2014-09-21" AND "2014-10-20"
But I can't figure out how to:
How can I query through this interval for say 1 year (or more)
How can I query for this month?
dynamically
The easiest way is to do date arithmetic. So, for October, you would use:
SELECT *
FROM rapoarte
WHERE month(DATE(ziua) - interval 20 day + interval 1 month) = 10
The idea is to subtract 20 days and add a month. This seems to be the logic for the reporting month.
You can add this as a field:
SELECT *,
date_format(month(DATE(ziua) - interval 20 day + interval 1 month), '%Y-%m') as ReportingMonth
FROM rapoarte
WHERE month(DATE(ziua) - interval 20 day + interval 1 month) = 10
EDIT:
If you want data for this reporting month:
WHERE month(DATE(ziua) - interval 20 day + interval 1 month) = month(DATE(now()) - interval 20 day + interval 1 month) and
year(DATE(ziua) - interval 20 day + interval 1 month) = year(DATE(now()) - interval 20 day + interval 1 month)

How to convert number of week into date?

Given a year and calendar week, how can I get the tuesday of that week as a date?
In MySQL the STR_TO_DATE() function can do the trick in just one line!
Example: We want to get the date of the Tuesday of the 32th week of the year 2013.
SELECT STR_TO_DATE('2013 32 Tuesday', '%X %V %W');
would output:
'2013-08-13'
I think this is the best and shortest solution to your problem.
Given you have year and cw (calender week) as variables (e.g. from a SELECT statement) you can get the DATE as following:
DATE_SUB(
DATE_ADD(MAKEDATE(year, 1), INTERVAL cw WEEK),
INTERVAL WEEKDAY(
DATE_ADD(MAKEDATE(year, 1), INTERVAL cw WEEK)
) -1 DAY),
The phrase DATE_ADD(MAKEDATE(year, 1), INTERVAL cw WEEK) is duplicated; did not want to store a variable. The SQL-Statement worked nicely for me on MySQL.
UPDATE: Just for clarification: WEEKDAY(DATE_ADD(MAKEDATE(year, 1), INTERVAL cw WEEK)) will yield the first day of the week. Substracting a number from it (-1 for Tuesday; -2 for Wednesday and so forth will select a specific day in the week for you).
See here.
The definitions of calendar week I found all said "a period of seven consecutive days starting on Sunday".
The following is MySQL specific... your mileage may vary...
DATE_ADD(MAKEDATE(year, 1), INTERVAL cw WEEK) adds the weeks from the 1st of the year which is not correct...
mysql> select DATE_ADD(MAKEDATE(2011, 1), INTERVAL 1 WEEK);
+----------------------------------------------+
| DATE_ADD(MAKEDATE(2011, 1), INTERVAL 1 WEEK) |
+----------------------------------------------+
| 2011-01-08 |
+----------------------------------------------+
By this definition, it is only meaningful to have the calendar week range from 1-53, and have this represent the Sunday of that week. As such, we would add 2 days to the nth Sunday of the year to get Tuesday.
The following gets the date of the first sunday of the year...
mysql> select date_add('2012-01-01', interval (8 - dayofweek('2011-01-01')) % 7 DAY);
+------------------------------------------------------------------------+
| date_add('2012-01-01', interval (8 - dayofweek('2011-01-01')) % 7 DAY) |
+------------------------------------------------------------------------+
| 2012-01-02 |
+------------------------------------------------------------------------+
so this will get the date of the 10th sunday (note interval 9 week since we are already at 1)...
mysql> select date_add( date_add('2010-01-01', interval (8 - dayofweek('2010-01-01')) % 7 DAY) , interval 9 week);
+-----------------------------------------------------------------------------------------------------+
| date_add( date_add('2010-01-01', interval (8 - dayofweek('2010-01-01')) % 7 DAY) , interval 9 week) |
+-----------------------------------------------------------------------------------------------------+
| 2010-03-07 |
+-----------------------------------------------------------------------------------------------------+
add 2 more days to get to tuesday...
mysql> select date_add( date_add( date_add('2010-01-01', interval (8 - dayofweek('2010-01-01')) % 7 DAY) , interval 9 week), interval 2 day);
+--------------------------------------------------------------------------------------------------------------------------------+
| date_add( date_add( date_add('2010-01-01', interval (8 - dayofweek('2010-01-01')) % 7 DAY) , interval 9 week), interval 2 day) |
+--------------------------------------------------------------------------------------------------------------------------------+
| 2010-03-09 |
+--------------------------------------------------------------------------------------------------------------------------------+
or more generally:
select
date_add(
date_add(
date_add('<year>-01-01', interval (8 - dayofweek('<year>-01-01')) % 7 DAY)
, interval <week-1> week)
, interval <dayOfWeek> day
);
In looking at indago's answer and then doing a bunch of tests, I was getting the following week as the results.
I've made a minor adjustment, and the dates then matched:
SELECT STR_TO_DATE('2019 1 Monday', '%x %v %W') -- beginning of week
SELECT STR_TO_DATE('2019 1 Sunday', '%x %v %W') -- end of week
You can compare the results with here.
DELIMITER $$
CREATE FUNCTION fn_yearweek_to_date(
var_yearweek INTEGER UNSIGNED,
var_weekday ENUM(
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
'Sunday'
)
)
RETURNS DATE DETERMINISTIC
BEGIN
RETURN STR_TO_DATE(CONCAT(var_yearweek, var_weekday), '%x%v%W');
END;
DELIMITER ;
SELECT
fn_yearweek_to_date(YEARWEEK(NOW(), 1), 'Sunday'),
fn_yearweek_to_date(YEARWEEK(NOW(), 1), 7)
;
Well theoretically you could use DATEPART with the dw parameter to get to find the first tuesday of the month and then add 7*[CalenderWeek] to get the appropriate date
http://msdn.microsoft.com/en-us/library/ms174420.aspx
I think it'd be easier to write the logic of the function using php.
If you use a php script, you can put all dates in a format similar to "day-month-year" and use a loop to go through every day (from 1980s to 2038 or from your mysql dates column).
http://www.php.net/manual/en/function.date-format.php
Then use date format on the dates in that loop to convert them to the days of the week.
Here is a listing of things that can be used in date formats. http://www.php.net/manual/en/function.date.php
D
N
l
w
all help you with day of the week.
Given solutions doesn't consider, that the first week of a year may start at the end of december. So we must check, if January 1st belongs to calendarweek of old or new year:
SET #week=1;
SET #year=2014;
SET #x_weeks_after_new_year=DATE_ADD(MAKEDATE(#year, 1), INTERVAL (SELECT IF(WEEKOFYEAR(MAKEDATE(#year, 1))>50 , 0 , -1))+#week WEEK);
SELECT
CONCAT(#year, '-', #week) WeekOfYear,
#weekStart:=DATE_SUB(#x_weeks_after_new_year, INTERVAL WEEKDAY(#x_weeks_after_new_year) DAY) Monday,
DATE_ADD(#weekStart, INTERVAL 6 DAY) Sunday
This will result in:
+------------+------------+------------+
| WeekOfYear | Monday | Sunday |
+------------+------------+------------+
| 2014-1 | 2013-12-30 | 2014-01-05 |
+------------+------------+------------+
Here is a sample that might help:
SET DATEFIRST 1
declare #wk int set #wk = 33
declare #yr int set #yr = 2011
select dateadd (week, #wk, dateadd (year, #yr-1900, 0)) - 2 -
datepart(dw, dateadd (week, #wk, dateadd (year, #yr-1900, 0)) - 4) as date
and the result is:
2011-08-16 00:00:00.000
which is today (Tuesday).
The upvoted solution worked for me in 2014 and 2015 but did not work for me in 2016 (possibly because the start of the Year is on Monday and not on Sunday.
I used the following function to correct this:
STR_TO_DATE(
CONCAT(mod(day_nr + 1 ,7) , '/', week_nr, '/', year), '%w/%u/%Y')
In my data :
day_nr = 0 -> Monday,
day_nr = 6 -> Sunday
So I had to fix that with a mod function