SQL SELECT logic - mysql

I've been having a bit of trouble thinking this problem through. I can't seem to define a SELECT query which is accurate enough to give me the result I want.
I am storing shift patterns in a table. These shift patterns don't have any restrictions on when they can start and finish (except that they cannot overlap each other per machine)
This is the structure of the table (5 rows of example data)
The only information I have to select with is:
The current time (e.g. 01:45)
The current weekday (e.g. Tuesday)
The issue is when a shift overlaps 00:00. So my question is this:
How would I select the current shift based on the current time and weekday?
Here is an SQL Fiddle of the scenario
Thanks!

You can do this with simple logic. If StartTime < EndTime, then you want to test for times between the two values. If Startime > EndTime, then you want to test for times not between the two values. So this solves the time problem:
SELECT *
FROM webreportshiftsetup
WHERE (StartTime < EndTime and time(now()) between StartTime and EndTime or
StartTime > EndTime and time(now()) not between StartTime and EndTime
) and
dayname(now()) in (StartWeekDay, EndWeekDay)
You have a similar problem with the weekdays. But your question is specifically about times and not weekdays. (That should perhaps be another question.)

If your shifts are one-day (i.e. you need to select only current day) you can do something like
SELECT * FROM shifts
WHERE startWeekDay = DATE_FORMAT(CURDATE(),'%W') AND NOW() BETWEEN startTime AND endTime
Otherwise, if your shift starts on Monday and finishes on Wednesday, and today is Tuesday, you will have trouble finding todays shift with a query. For that you should store days as number: 1- Monday, 2- Friday, ...

I would suggest that you change the schema. One option is to use the datetime type for start/end, instead of using varchar for everything.
Then you can query it like:
SELECT *
FROM webreportshiftsetup
WHERE NOW() BETWEEN StartDateTime AND EndDateTime
and so forth.
If you need this to be a repeating thing to where specific dates won't work, then you might make the StartDay/EndDay columns tinyint and give them a value of 1-7, with the smallest number being the first day of the week and the largest number representing the last day of the week. StartTime/EndTime would be date type. Querying that would look like:
SELECT *
FROM webreportshiftsetup
WHERE StartDay >=2 AND EndDay <=4 AND StartTime >= '2:00' AND EndTime <= '13:00'

Related

Given a range of dates, how to capture rows that has at least one of those dates in another range of dates? [duplicate]

I have a table of events with a recorded start and end time as a MySQL DATETIME object (in the format YYYY-MM-DD HH:MM:SS. I want to find all events that occur in a specific date range. However, events can span multiple days (and go outside of my date range, but I want to return them if they even overlap by 1 second or more with my date range).
Suggestions?
This will find every event that is completely contained inside the range:
SELECT * FROM table WHERE start_date BETWEEN start_of_range AND end_of_range
AND stop_date BETWEEN start_of_range AND end_of_range
This will find any events where any part of the event overlaps any part of the range:
SELECT * FROM table WHERE start_date <= end_of_range
AND stop_date >= start_of_range
The answers by #Bill the Lizard and #Robert Gamble are correct for the question as asked, but I do wonder if you're asking what you think you are... If you're looking for overlapping events then you need to take into account events longer than your search range.
Monday Tuesday Wednesday Thursday
Search: |-----------|
Shopping |-----| Found OK
Eating |--------| Found OK
Stack Overflow |---------------------------------| Not found!
If you wanted to include SO, you'd do:
SELECT * FROM table WHERE (start_date < end_of_range AND end_date > start_of_range)
SELECT *
FROM table
WHERE startdate >= 'starting date' AND startdate < 'ending date'
OR enddate >= 'starting date' AND enddate < 'ending date'
should work for you.
Make sure you specify 'starting date' and 'ending date' with the time included.
'2008-01-01 00:00:00'' AND '2008-01-31 23:59:59'
This will help to avoid errors where dates are the same, but your time falls within the interval by a few hours, minutes, or seconds.
Basically, you can use regular comparisons -- the ones above should work -- the trick is to check all the different cases that can occur.
A) events with an ending date within the range
B) events with a starting date within the range
C) events with both starting and ending dates within the range
D) events with both starting and ending dates outside the range, but overlapping it
Robert's answer is a good one, but it doesn't take into account case D, where the event starts before the range and ends after the range.
Llya, Roberts answer with,
SELECT * FROM table WHERE start_date <= end_of_range
AND stop_date >= start_of_range
works fine with,
D) events with both starting and ending dates outside the range, but overlapping it
??

Efficient SELECT query to find records within a month

I have a MySQL DB table with multiple date type fields. I need to do different SELECT queries on this table but I am not sure which way is the best to find records from the same month.
I know I can do the following:
SELECT *
FROM table
WHERE MONTH(somedate) = 5
AND YEAR(somedate) = 2015
But I keep reading that isn't efficient and that I should go with using actual dates, i.e.
SELECT *
FROM table
WHERE somedate BETWEEN '2015-05-01' AND '2015-05-31'
However, all I would have is the month and the year as variables coming in from PHP. How do I easily and quickly calculate the last day of the month if I go with second option?
Don't calculate the last day of the month. Calculate the first day of the next month instead.
Your query can be like this
WHERE t.mydatetimecol >= '2015-05-01'
AND t.mydatetimecol < '2015-05-01' + INTERVAL 1 MONTH
Note that we're doing a less than comparison, not a "less than or equal to"... this is very convenient for comparing TIMESTAMP and DATETIME columns, which can include a time portion.
Note that a BETWEEN comparison is a "less than or equal to". To get a comparison equivalent to the query above, we'd need to do
WHERE t.mydatetimecol
BETWEEN '2015-05-01' AND '2015-05-01' + INTERVAL 1 MONTH + INTERVAL -1 SECOND
(This assumes that the resolution of DATETIME and TIMESTAMP is down to a second. In other databases, such as SQL Server, the resolution is finer than a second, so there we'd have the potential of missing a row with value of '2015-05-31 23:59:59.997'. We don't have a problem like that with the less than the first day of the next month comparison... < '2015-06-01'
No need to do the month or date math yourself, let MySQL do it for you. If you muck with adding 1 to the month, you have to handle the rollover from December to January, and increment the year. MySQL has all that already builtin.
date('t', strtotime("$year-$month-01")) will give days in the month

mySQL gather data between hours of the day passing through midnight with date increment

I am trying to get data from a database between 8PM (say, today) and 2AM tomorrow.
I have been using clauses such as where hour(date_field)>=20 and hour(date_field) <23 to obtain data in the same day.
Here the date_field is datetime
All I want is to be able to tell SQL to get data after 8PM today, increment the datefield and then get data till 2AM tomorrow.
Any help will be appreciated.
The normal pattern for retrieving rows based on a datetime range is perform comparisons on the bare column, comparing the column value to constants derived from expressions.
To get rows for a single contiguous range, 8PM today to 2AM tomorrow, for example:
WHERE t.date_column >= DATE(NOW()) + INTERVAL 20 HOUR
AND t.date_column < DATE(NOW()) + INTERVAL 26 HOUR
To unpack that a little bit: NOW() returns current datetime, the DATE() function truncates the time portion to midnight, then we add back in enough hours to get '8PM today', or enough hours to get '2AM tomorrow'.
If you are meaning to retrieve multiple "8PM to 2AM" periods, for a whole series of days.
First, you'd want an upper and lower bound of the date_column to be retrieved (unless you want every possible date)
WHERE t.date_column >= '2014-08-01 20:00:00'
AND t.date_column < '2014-10-02 02:00:00'
From that, we need to filter out all of the rows that aren't between 8PM and 2AM. One convenient way to do that would be to "subtract" two hours from the datetime col, and check for hour >= 6PM.
AND HOUR(t.date_column + INTERVAL -2 HOUR) >= 18
Note that the expression involving date_column will need to be evaluated for EVERY row in the table, unless there are some other predicates that filter rows out. With a suitable index available, MySQL can use an index range scan operation for predicates of the form date_column >= const and date_column < const. (It can't do that when the column is wrapped in a function or expression.)

How best to store time ranges like Tuesday 10-11am?

It's not a one-of, not only one particular Tuesday, so I won't use time stamps.
I mean something like "every Tuesday from 10am to 11am". What's the best way to store this to make it easy to check from my (Delphi) application if the current time is between those permitted times?
Sounds like the time range would simply be an attribute of whichever primary object you are working with, as such you could add a separate table to store the time range data, something like:
object_time_range
id
object_id
day
hour_start
hour_end
frequency_id
-- Update --
In hindsight I would probably abstract hour from the column names in favor of time, just in case you need to consider alternative time frames. And day should be day_id, even though its a static list, I always like going with ints in this situation.
object_time_range
id
object_id
day_id
time_start
time_end
frequency_id
Store the variable as datetime or timestamp,
you can use mysql date functions such as
date_format and date_add
to get the rows which fall within required dates and time
if you store it as text it would be very difficult.
E.g To get rows for the past one day
select * from tableName
where dateField > date_add(now(),interval -1 day) and
dateField < now()
rows for the past one Week
select * from tableName
where dateField > date_add(now(),interval -1 week) and
dateField < now()
Check date_add funciton

finding events from previous Sunday to next Sunday

How can you find MySQL data for the current week plus the following Sunday?
Given a date (e.g. Wednesday 5/18/11), it would show events from the previous Sunday to the next Sunday. 5/15/11 through 5/22/11.
The trick would be to find the 'previous' Sunday to a given date.
How can this be done?
SELECT *
FROM events
WHERE Yearweek(`eventdate`) = Yearweek(NOW())
OR ( Weekday(NOW()) = 6
AND Yearweek(`eventdate`) = Yearweek(
DATE_SUB(NOW(), INTERVAL 1 DAY)) )
Taking from Pentium's answer, with some adjustments...
SELECT
*
FROM
Events
WHERE
YEARWEEK(`eventdate`) = YEARWEEK(NOW()) OR
(
WEEKDAY(`eventdate`) = 6 AND
YEARWEEK(`eventdate`) = YEARWEEK(NOW()) + 1
)
This may need to be adjusted depending on the values for WEEKDAY (is 6 Sunday?).
Also, while this should work, my guess is that mySQL won't be able to use any indexes on the eventdate column with this method. It's probably better to find the actual dates themselves for the bordering Sundays and then do a BETWEEN or <= >=. This should allow the use of an index on the eventdate. Even if you don't have an index on it now, you might want to use one in the future.
Using a calendar table . . .
select cal_date
from calendar
where cal_date between
(select max(cal_date) from calendar
where cal_date <= '2011-05-15' and day_of_week = 'Sun') and
(select min(cal_date) from calendar
where cal_date > '2011-05-15' and day_of_week = 'Sun')
It's not clear what you want if the given date is a Sunday. This previous query returns 15 rows given a date that falls on Sunday. It returns 8 rows for all other days. You can tweak the comparison operators in the WHERE clause to get the behavior you want.
I posted code for a calendar table earlier on SO. It's for PostgreSQL, but you should be able to adapt it to MySQL without much trouble.