We are using MySQL as our database to store messages with timestamps. Is it possible to create a query that returns messages of the last n weekdays?
I.e. if n is 4 and today is Tuesday, I want messages from this weeks Monday, last weeks Friday, last weeks Thursday and last weeks Wednesday .
If you want to do this directly with mysql it would be a little complicated. As Vatev recommended you should calculate date star date in advance, but if you really want to do this, you'll probably need following functions:
ADD_DATE, with INTERVAL -N WEEKS
FLOOR, in C int/int would do just fine
MOD, a % b :)
WEEKDAY
First of all you need should count how many weeks you should go back, that's easy... For you one week = 5 days, that means
weeks = FLOOR(days / 5)
We've taken care of weeks, so we'll now have to work with the rest:
rest = days MOD 5
Now we have two cases, weekend has occurred or no, for the case that there wasn't weekend days are good. We have to add 2 days to skip it. The weekend occurred if (WEEKDAY(now) - rest) < 0
rest = IF( (WEEKDAY(now) - rest) < 0, rest + 2, rest)
And now we can build it to one par (let's assume you have {days} and {rest} pre-calculated):
WHERE date >= ADD_DATE(
ADD_DATE (
{now},
INTERVAL -IF( (WEEKDAY({now}) - {rest}) < 0, {rest} + 2, {rest}) DAYS,
),
INTERVAL -FLOOR({days} / 5) WEEKS
)
The best i can come up with is calculating the start date ({start_date} in the query) in the language of your choice and then running something like this:
SELECT some_things
FROM your_table
WHERE
WEEKDAY(time_column) < 5
AND time_column >= {start_date}
ORDER BY time_column DESC
You can also make a stored function to calculate 'the date x week days ago' and use it for {start_date}.
Have you tried something like this?
SELECT columns
FROM table
WHERE datediff(column_with_timestamp,NOW()) > n
Where N is as you defined above, the number of days you're looking for.
COL >= date_sub( NOW(), interval 1 week) and
weekday( COL ) < 5
date_sub is to seek rows created last week
weekday is to exclude sunday or saturday
Related
I'm trying to calculate the week number of a quarter in MySQL.
Unfortunately, all existing solutions are based on DATEDIFF between two dates which is not what I want.
For example, today's date is Feb 24th. It is week 9 in the quarter as the first three days of January (Friday, Saturday and Sunday in this current year) were week 1.
If I do a DateDiff and simply divide by 7, it's still only giving me the number of weeks total by days. It's not giving me the calendar weeks since the start of the quarter.
CEIL(DATEDIFF(CURDATE(), MAKEDATE(YEAR(CURDATE()), 2) + INTERVAL QUARTER(CURDATE()) QUARTER - INTERVAL 1 QUARTER) /7) AS WEEK_IN_QUARTER,
This gives me week 8, when the expected (wanted) output is 9.
Adding 1 to the week is going to cause problems when the opposite is true and when there's only a partial week left in the quarter or when we're 4 days into any week.
The first week should start on the first day of the quarter and end on the Sunday of that week.
I've been through every MySQL DATE function and I'm really stuck.
I normally do this on the client side using Python (see below) but I'd love to know if there's an easy way to do this in MySQL before trying to translate this. Any Help would be very much appreciated.
Python code if it makes what I'm trying to say a bit clearer:
from datetime import timedelta, date
from dateutil import relativedelta
NEXT_MONDAY = relativedelta.relativedelta(weekday=relativedelta.MO)
LAST_MONDAY = relativedelta.relativedelta(weekday=relativedelta.MO(-1))
ONE_WEEK = timedelta(weeks=1)
def get_week_in_quarter(dt):
d: date = dt.date()
year = d.year
# Q0 = January 1, Q1 = April 1, Q2 = July 1, Q3 = October 1
quarter = ((d.month - 1) // 3)
quarter_start = date(year, (quarter * 3) + 1, 1)
quarter_week_2_monday = quarter_start + NEXT_MONDAY
if d < quarter_week_2_monday:
week = 1
else:
cur_week_monday = d + LAST_MONDAY
week = int((cur_week_monday - quarter_week_2_monday) / ONE_WEEK) + 2
if quarter == 0:
year -= 1
quarter = 4
return week
EDIT 1:
To further clarify, if I separate out the weeks and days on a transition day (which today is), you can see the problem:
SET #day1=DATE(CURDATE());
SET #day2=DATE(MAKEDATE(YEAR(CURDATE()), 2) + INTERVAL QUARTER(CURDATE()) QUARTER - INTERVAL 1 QUARTER);
SELECT CONCAT(SUBSTRING_INDEX(CEIL(DATEDIFF(#day1,#day2)/7),'.',1),'Weeks ',
SUBSTRING_INDEX(ABS(DATEDIFF(#day1,#day2)),'.',1)-SUBSTRING_INDEX(ROUND(DATEDIFF(#day1,#day2))/7,'.',1)*7,'Days'
)AS WEEKINQUARTER
The output is:
8Weeks 4Days
I don't know how to produce the required output (just "9") by dividing the days difference by 7. In Python, I have to set a delta to the last Monday to account for that. I don't know how to do that in MySQL either.
EDIT 2
Strawberry's answer below solved this for me in SQL. I ended up using CEIL instead of floor but for anyone looking for the current Fiscal Quarter week calculation in SQL in the future, this is a solution:
Does this work?
SELECT FLOOR(DATEDIFF('2021-02-24',CONCAT(DATE_FORMAT(LAST_DAY(MAKEDATE(EXTRACT(YEAR FROM '2021-02-24'),1) + INTERVAL QUARTER('2021-02-24')*3-3 MONTH),'%Y-%m-'),'01') - INTERVAL WEEKDAY(CONCAT(DATE_FORMAT(LAST_DAY(MAKEDATE(EXTRACT(YEAR FROM '2021-02-24'),1) + INTERVAL QUARTER('2021-02-24')*3-3 MONTH),'%Y-%m-'),'01')) DAY)/7) x;
week() with mode 3 is the ISO week, which is what I think you want.
You can get the week of the quarter by using arithmetic. The first 13 weeks are in Q1, 14-26 in Q2, 27-39 in Q3 and the rest in Q4.
So:
select (case when week(date, 3) <= 13 then week(date, 3)
when week(date, 3) <= 26 then week(date, 3) - 13
when week(date, 3) <= 39 then week(date, 3) - 26
else week(date, 3) - 39
end)
There are tricks you can use to simplify this using modulo arithmetic, but I think this is clearest for explaining what to do.
I am writing annual membership registrations to a single db table. I need to keep track of when renewals have occurred in less than 11 months from their last renewal.
I look for the duplicate rows based on multiple criteria. I currently have this working with out the 11 month criteria, although it's slow. Here's what I currently use.
SELECT y_reg.* FROM y_reg WHERE (((y_reg.season) In (SELECT season FROM y_reg As Tmp
GROUP BY season, Father_Last_Name, Father_First_Name
HAVING Count(*)>1
AND Father_Last_Name = y_reg.Father_Last_Name
AND Father_First_Name = y_reg.Father_First_Name)))
ORDER BY y_reg.season, y_reg.Father_Last_Name, y_reg.Father_First_Name
I have a field Date which is the date of the renewal that I need to evaluate. I'd like to add something like "AND Date - Date < 335"
335 is the number of days and is about 1 month short of a year. But I just keep getting syntax error because I clearly don't know what I'm doing.
Date arithmetic works quite well in MySQL; you just need the knack.
You can say things like
AND later.Date >= earlier.Date
AND later.Date < earlier.Date + INTERVAL 11 MONTH
That particular pair of comparisons comes up true if the later date occurs in the time range between the earlier date and 11 months later.
In general you can say stuff like this to do date arithmetic.
datestamp + INTERVAL 1 HOUR
datestamp - INTERVAL 5 MINUTE
datestamp + 1 MONTH - 3 WEEK
datestamp - INTERVAL 3 QUARTER (calendar quarters)
LAST_DAY(datestamp) + INTERVAL 1 DAY - INTERVAL 1 MONTH
The last item is the first day of the month containing the datestamp. This whole date thing works quite well.
I think you should consider a so-called self-join query to get your duplicate-except-for-date results. Try something like this.
SELECT a.*
FROM y_reg a
JOIN y_reg b ON a.Father_Last_Name = b.Father_Last_Name
AND a.Father_First_Name = b.Father_First_Name
AND b.Date < a.Date - 11 MONTH
AND b.Date >= a.Date - 12 MONTH
I want to retrieve data whose Date between start and end date is between today and after ten day.I'm doing it in rails but even if i get the query in MySQL i can convert it to rails active record.
Something like this one :
select * from users where( between users.from and users.to = between '2012-11-25 11:52:33' and '2012-12-05 11:52:33')
The interval (a, b) overlaps with (c, d) iff a < d and b > c. Also, the curdate() function returns the current date ("today"). To calculate the date ten days into the future you can use + interval 10 day.
Combining these bits of information you get:
select ... where users.to > curdate()
and users.from < curdate() + interval 10 day
Follwing is the logic snippet not the exact code though. Please try.
select * from users
where users.fromDate between '2012-11-25 11:52:33' and '2012-12-05 11:52:33'
and users.toDate between '2012-11-25 11:52:33' and '2012-12-05 11:52:33';
Check the following sample code in * SQLFIDDLE as well
EDITING AS PER YOUR QUESTION'S REPHRASING..
Guess you are better off with,
where users.from between users.from and users.from + interval 10day
and users.to <= user.from + interval 10 day
That means, your from date can be anything (today, yesterday, one month back, two years to future...). Hence your to-date will be validated against any date that is 10 days interval from the above from-date
Hope it makes sense... but again, Joni's answers fills for your today's consition. :)
I'm trying to query through historical data and I need to return data just from a 1 month period: 2 weeks back and 2 weeks forward,but I need the year to not matter.
So, if I was to make the query today I would want all rows with date between xxxx-06-31 and xxxx-07-27
Thanks in advance for the help!
EDIT:
I've tried two ways. both of which I believe will not work around the new year. One is to use datepart(day) and the other would be to simply take the year off of date and compare.
The best way to think of this problem is to convert your dates to a number between 0 and 365 corresponding to the day in the year. Then simply choosing dates where this difference is less than 14 gives you your two week window.
That will break down at the beginning or end of the year. But simple modular arithmetic gives you the answer.
Fortunately, MySQL has DAYOFYEAR(date), so it's not so complicated:
SELECT * FROM tbl t
WHERE
MOD(DAYOFYEAR(currdate) - DAYOFYEAR(t.the_date) + 365, 365) <= 14
OR MOD(DAYOFYEAR(t.the_date) - DAYOFYEAR(currdate) + 365, 365) <= 14
That extra + 365 is needed since MySQL's MOD will return negative numbers.
This answer doesn't account for leap years correctly. If the current year is not a leap year and the currdate is within 14 days of the end of the year, then you'll miss one day in Jan that you should have included. If you care about that, then you should replace 365 with [the number of days in the year - 1].
Supposed you have a date like this,
create table datelist
(
d date
);
insert into datelist values
('2012-07-01'),
('2011-06-29'),
('2012-07-02'),
('2010-07-05'),
('2012-05-31'),
('2010-06-30');
Try this query below,
SELECT d, date_format(d,'%Y-%b-%d')
FROM datelist
WHERE (MONTH(d) = 6 AND DAYOFMONTH(d) >= 30)
OR (MONTH(d) = 7 AND DAYOFMONTH(d) <= 27)
SQLFiddle Demo
Is it OK if the solution is terribly slow?
SELECT tbl.*
FROM tbl
INNER JOIN (SELECT COALESCE(DATE(CONCAT(yyyy, '-', MONTH(CURRENT_DATE), '-', DAYOFMONTH(CURRENT_DATE)),
DATE(CONCAT(yyyy, '-02-28'))) AS midpoint
FROM (SELECT DISTINCT(YEAR(d)) AS yyyy
FROM tbl) all_years) adjusted
ON tbl.datecol BETWEEN adjusted.midpoint - INTERVAL 2 WEEK
AND
adjusted.midpoint + INTERVAL 2 WEEK;
That computes all midpoints for all years in the data set, and then pulls records +- 2 weeks from any such midpoint, which handles end-of-year wrapping.
The COALESCE handles 02-29 on years without leapday (which MySQL will NULL-ify), forcing it down to 02-28.
I have a report that is driven by a sql query that looks like this:
SELECT batch_log.userid,
batches.operation_id,
SUM(TIME_TO_SEC(ramses.batch_log.time_elapsed)),
SUM(ramses.tasks.estimated_nonrecurring + ramses.tasks.estimated_recurring),
DATE(start_time)
FROM batch_log
JOIN batches ON batch_log.batch_id=batches.id
JOIN ramses.tasks ON ramses.batch_log.batch_id=ramses.tasks.batch_id
JOIN protocase.tblusers on ramses.batch_log.userid = protocase.tblusers.userid
WHERE DATE(ramses.batch_log.start_time) > "2011-02-01"
AND ramses.batch_log.time_elapsed > "00:03:00"
AND DATE(ramses.batch_log.start_time) < now()
AND protocase.tblusers.active = 1
AND protocase.tblusers.userid NOT in ("ksnow","smanning", "dstapleton")
GROUP BY userid, batches.operation_id, date(start_time)
ORDER BY start_time, userid ASC
Since this is to be compared with the time from the current payperiod it causes an error.
Our pay periods start on a Sunday, the first pay period was 2011-02-01 and our last pay period started the 4th of this month. How do I put that into my where statement to strip the most recent pay period out of the query?
EDIT: So now I'm using date_sub(now(), INTERVAL 2 WEEK) but I really need a particular day of the week(SUNDAY) since it is wednesday it's chopping it off at wednesday.
You want to use DATE_SUB, and as an example.
Specifically:
select DATE_SUB(curdate(), INTERVAL 2 WEEK)
gets you two weeks ago. Insert the DATE_SUB ... part into your sql and you're good to go.
Edit per your comment:
Check out DAYOFWEEK:
and you can do something along the lines of:
DATE_SUB(DATE_SUB(curdate(), INTERVAL 2 WEEK), INTERVAL 2 + DAYOFWEEK(curdate()) DAY)
(I don't have a MySql instance to test it on .. but essentially subtract the number of days after Monday.)
Question isn't quite clear, especially after the edit - it isn't clear now is the "pay period" two weeks long or do you want just last two weeks back from last sunday? I assume that the period is two weeks... then you first need to know how many days the latest period (which you want to ignore, as it isn't over yet) has been going on. To get that number of days you can use expression like
DATEDIFF(today, FirstPeriod) % 14
where FirstPeriod is 2011-02-01. And now you strip that number of days from the current date in the query using date_sub(). The exact expression depends on how the period is defined but you should get the idea...