How to test if there are overlapping periods with a particular period? - mysql

In my table there are two columns of type date and two columns of type time :
Here are some records of the table :
Now , in my web app I want to insert a new row in that table :
When submitting the form I want to count the number of rows where the entered period overlaps to others that are already in the database table ; by period I mean a ( beginning_date , beginning_time ) and a ( ending_date , ending_time ) together , for example ( 2016-03-15 , 12:00:00 ) and ( 2016-03-17 , 10:00:00 ).
I tried this query but it does not give the right results :
select count(identifiant) from reservation_table where ( (date_debut <= '2016-03-14' and heure_debut <= '01:00:00') and (date_fin <= '2016-03-14' and heure_fin <= '03:00:00') and (date_fin > '2016-03-14' and heure_fin > '01:00:00') ) or
( (date_debut >= '2016-03-14' and heure_debut >= '01:00:00') and (date_fin >= '2016-03-14' and heure_fin >= '03:00:00') and (date_debut < '2016-03-14' and heure_debut < '03:00:00') ) or
( (date_debut >= '2016-03-14' and heure_debut >= '01:00:00') and (date_fin <= '2016-03-14' and heure_fin <= '03:00:00') ) or
( (date_debut <= '2016-03-14' and heure_debut <= '01:00:00') and (date_fin >= '2016-03-14' and heure_fin >= '03:00:00') );
To have a better understanding about the period overlapping here is an image :
So in this image the red period is the period entered from the web app , and the black periods are those already in the database. So how to get all periods that overlap to a particular period ?

The way to test if two elements overlap is to check if the one starts before the second ends, while the second starts before the first ends, as mentioned in the overlap tag wiki.
I don't have much experience with MySql but did find this method to create a datetime value from date and time:
STR_TO_DATE(CONCAT(date, ' ', time), '%Y-%m-%d %H:%i:%s')
Once you have datetime values you can do this:
select count(identifiant)
from reservation_table
where #YourStartDatetime <= STR_TO_DATE(CONCAT(date_fin,' ', heure_fin), '%Y-%m-%d %H:%i:%s')
and #YourEndDateTime >= STR_TO_DATE(CONCAT(date_debut ,' ', heure_debut), '%Y-%m-%d %H:%i:%s')
if the count returns 0, then you have no records overlapping the period specified by #YourStartDatetime and #YourEndDateTime

Related

Select all stores open at current time

I have a table named opening_hours that look like this:
id int(11), weekday int(1), start_hour time, end_hour time
I use this query to select all stores that are open now:
SELECT * FROM shops s
INNER JOIN opening_hours o
ON ( s.id = o.id )
AND ( o.weekday = WEEKDAY(CURDATE()) + 1 )
AND ( ( CURTIME() >= o.start_hour ) AND ( CURTIME() <= o.end_hour ) )
My problem is that this query is giving the wrong result when stores are open after midnight. That's because time after midnight is earlier than the time before midnight. How to handle this?
The correct logic is more complicated. When the opening hours span two dates, then in the wee hours of the morning, you have to look at the previous days opening hours -- unless you assume that the hours are the same for each day (but then why have table?).
The condition is more like this:
SELECT *
FROM shops s INNER JOIN
opening_hours o
ON s.id = o.id
WHERE ((o.weekday = weekday(curdate()) + 1) and
((o.end_hour > o.start_hour and curtime() >= o.start_hour and curtime <= o.end_hour) or
(o.end_hour < o.start_hour and curtime() >= o.start_hour)
) or
(o.weekday = weekday(date_add(curdate(), interval 1 day)) + 1 and
o.end_hour < o.start_hour and
curtime() <= o.end_Hour
);
The three conditions are:
Opening hours all in one day
Opening hours span two days, and the hour not earlier than the opening
Opening hours span two days, and the hour is earlier than the closing hour
When the end_hour is less than start_hour then you have to modify the end_hour so that it becomes greater than start_hour.
One way to achieve this is to deduct end_hour from 24:00:00 when end_hour is less than start_hour otherwise end_hour prevails.
SELECT * FROM shops s
INNER JOIN opening_hours o
ON ( s.id = o.id )
AND ( o.weekday = WEEKDAY(CURDATE()) + 1 )
AND ( ( CURTIME() >= o.start_hour ) AND ( CURTIME() <= IF(o.end_hour < o.start_hour,TIMEDIFF(TIME('24:00:00'),o.end_hour),o.end_hour ) ) ) ;
EDIT:
The query above performs great when the end_hour is 00:00:00.
But it might give wrong output for this case
start_hour = 07:00:00 & end_hour = 02:00:00.
So, here how you can recover (use this condition in your main query):
AND
(
IF(o.end_hour < o.start_hour,
( CURTIME() >= o.start_hour ) OR ( CURTIME() <= o.end_hour ),
( CURTIME() >= o.start_hour ) AND ( CURTIME() <= o.end_hour )
)
Note that if the range lies in the same date then this condition should prevail:
( CURTIME() >= o.start_hour ) AND ( CURTIME() <= o.end_hour ).
And if the range wraps over midnight then this condition should be in action:
( CURTIME() >= o.start_hour ) OR ( CURTIME() <= o.end_hour )

mysql to check room is exists for a particular date with time duration

I have check room is exists between two time for particular date.
I have try following two query its run some time rights but, when i select 10:00 AM to 12:00 PM at time wrong results means not return any records.
QUERY-1 :
SELECT 1 FROM `timetable_details` WHERE (
((`td_from` <= '10:00:00') AND (`td_to` > '10:00:00'))
OR
((`td_from` < '12:20:00') AND (`td_to` >= '12:20:00'))
) AND ((`td_room`='1') AND (`td_date`='2016-01-25'))
QUERY-2 :
SELECT 1 FROM `timetable_details` WHERE (
(`td_from` > '07:00:00') AND (`td_to` < '08:00:00')
) AND ((`td_room`='1') AND (`td_date`='2016-01-25'))
I have get td_id = 4 number row but is not returns.
You can use between with OR condition for both columns as below :
SELECT 1 FROM `timetable_details` WHERE (((((`td_from` BETWEEN '10:00:00' AND '12:30:00') OR (`td_to` BETWEEN '10:00:00' AND '12:30:00')) AND ((`td_room`='1') AND (`td_date`='2016-01-25') AND (`td_status` IS NULL))) AND (`td_from` <> '12:30:00')) AND (`td_to` <> '10:00:00'))

Why am i getting this error message ( #1111 - Invalid use of group function )?

I am trying to retrieve the TIMEDIFF from the last_call field of every row, where TIMEDIFF between current row and next row is greather than 10 min!
Can someone please help? Meybe there is a better way of doing this?
My goal is to get the total of all timediff between severall database entrys but only if they are greather than 10 min.
SELECT DATE_FORMAT( last_call, '%d' ) AS 'day',
(
SELECT TIME_TO_SEC(TIMEDIFF(MAX(cl1.last_call), MIN(cl1.last_call)))
FROM calls AS cl1
WHERE TIME_TO_SEC(TIMEDIFF(MAX(cl1.last_call), MIN(cl1.last_call))) > 600
AND cl1.calling_agent=9
AND EXTRACT(DAY FROM cl1.last_call ) = EXTRACT(DAY FROM calls.last_call )
) AS 'brake'
FROM calls
WHERE calling_agent =9
AND last_call > DATE_SUB( now( ) , INTERVAL 12 MONTH )
GROUP BY EXTRACT( DAY FROM last_call )

mysql get sum of hours / minutes / seconds

I have a table with: userid and timestamp each time a user opens a page a new field is inserted.
I am trying to get the total amount of hours / minutes / days / weeks that appear in a 1 month interval for multiple users.
I have tried a bunch of different queries but each have ended up terribly inefficient.
Ideally I'd like to end up with something like:
userid | minutes | hours | days | weeks
1 10080 168 7 1
2 1440 24 1 0
Hopefully someone can shed some light on how to do this.
Below is a query that I tried:
SELECT
w.time AS `week`,
d.time AS `day`,
h.time AS `hour`,
m.time AS `minutes`
FROM (
SELECT
SUM( t.time ) AS `time`
FROM (
SELECT
COUNT( DISTINCT WEEK( `timestamp` ) ) AS `time`
FROM table
WHERE
userid = "1"
AND
`timestamp` > DATE_SUB( NOW( ) , INTERVAL 1 MONTH )
GROUP BY MONTH( `timestamp` )
) t
) w,
(
SELECT
SUM( t.time ) AS `time`
FROM (
SELECT
COUNT( DISTINCT DAY( `timestamp` ) ) AS `time`
FROM table
WHERE
userid = "52"
AND
`timestamp` > DATE_SUB( NOW( ) , INTERVAL 1 MONTH )
GROUP BY MONTH( `timestamp` )
) t
) d,
(
SELECT
SUM( t.timestamp ) AS `time`
FROM (
SELECT
COUNT( DISTINCT HOUR( `timestamp` ) ) AS `time`
FROM table
WHERE
userid = "1"
AND
`timestamp` > DATE_SUB( NOW( ) , INTERVAL 1 MONTH )
GROUP BY DAY( `timestamp` )
) t
) h,
(
SELECT
SUM( t.timestamp ) AS `time`
FROM (
SELECT
COUNT( DISTINCT MINUTE( `timestamp` ) ) AS `time`
FROM table
WHERE
userid = "1"
AND
`timestamp` > DATE_SUB( NOW( ) , INTERVAL 1 MONTH )
GROUP BY HOUR( `timestamp` )
) t
) m
It seems awfully excessive for this task, maybe someone has something better?
It's not clear to me what you want to "total".
If you want to determine whether a user had a "hit" (or whatever transaction it is you are storing in the table) at any given minute within the month), and then you want to count the number of "minute periods" within a month that a user had a hit:
SELECT t.userid
, COUNT(DISTINCT DATE_FORMAT(t.timestamp,'%Y-%m-%d %H:%i')) AS minutes
, COUNT(DISTINCT DATE_FORMAT(t.timestamp,'%Y-%m-%d %H' )) AS hours
, COUNT(DISTINCT DATE_FORMAT(t.timestamp,'%Y-%m-%d' )) AS days
, COUNT(DISTINCT DATE_FORMAT(t.timestamp,'%X-%V' )) AS weeks
FROM mytable t
WHERE t.timestamp >= '2012-06-01'
AND t.timestamp < '2012=07-01'
GROUP BY t.userid
What this is doing is taking each timestamp, and putting it into a "bucket", by chopping off the seconds, chopping off the minutes, chopping off the time, etc.
Basically, we're taking a timestamp (e.g. '2012-07-25 23:15:30') and assigning it to
minute '2012-07-25 23:15'
hour '2012-07-25 23'
day '2012-07-25'
A timestamp of '2012-07-25 23:25:00' would get assigned to
minute '2012-07-25 23:25'
hour '2012-07-25 23'
day '2012-07-25'
Then we go through and count the number of distinct buckets we assigned a timestamp to. If that's all the hits for this user in the month, the query would return a 2 for minutes, and a 1 for all other period counts.
For a user with a single hit within the month, all the counts for that user will be a 1.
For a user that has all their "hits" within exactly the same minute, the query will again return a 1 for all the counts.
(For a user with no "hits" within a month, no row will be returned. (You'd need to join another row source to get a list of users, if you wanted to return zero counts.)
For a user with a "hit" every second within a single day, this query will return counts like that shown for userid 2 in your example.
This result set gives you a kind of an indication of a user's activity for a month... how many "minute periods" within a month the user was active.
The largest value that could be returned for "days" would be the number of days in the month. The largest possible value to be returned for "hours" would be 24 times the number of days in the month times. The largest possible value returned for "minutes" would be 1440 times the number of days in the month.
But again, it's not entirely clear to me what result set you want to return. But this seems like a much more reasonable result set than the one from the previously "selected" answer.
SELECT userid, SUM(MINUTE(timestamp)) AS minutes, SUM(MINUTE(timestamp))/60 AS hours, SUM(MINUTE(timestamp))/(60*24) AS days, SUM(MINUTE(timestamp))/(60*24*7) AS weeks
FROM Table
GROUP BY userid
If neccesary, use ROUND(SUM(MINUTE(timestamp)), 0) if you want integer numbers.

MySQL querying rostered shifts over a number of days - simpler way?

I have some reports to do with MySQL and the below code is my current solution to the problem.
It seems quite straight forward until I start considering when staff shifts go over midnight so the query has to be adjusted. Right now I have php generate different queries depending on the date/time span as follows:
If the work shift falls whithin the same day (ie: 8am-8pm across 2 days):
SELECT <select statements>
FROM <from statements>
WHERE
(
(Date = '2012-04-16' AND Time BETWEEN '08:00:00' AND '20:00:00')
OR
(Date = '2012-04-02' AND Time BETWEEN '08:00:00' AND '20:00:00')
);
If the shift goes over midnight it gets complex (ie: 8pm-8am across 2 days):
SELECT <select statements>
FROM <from statements>
WHERE
(
(
(Date = '2012-04-16' AND Time >= '20:00:00')
OR
(Date = '2012-04-17' AND Time <= '08:00:00')
)
OR
(
(Date = '2012-04-17' AND Time >= '20:00:00')
OR
(Date = '2012-04-18' AND Time <= '08:00:00')
)
);
As you can imagine, these queries get really long and heavy with every single day I add to the report. There must be a smarter way to do this - could anyone offer any insight?
If you want to have more elegant code, this part:
(Date = '2012-04-16' AND Time >= '20:00:00')
OR
(Date = '2012-04-17' AND Time <= '08:00:00')
could be changed into:
(Date, Time) >= (DATE('2012-04-16'), TIME('20:00:00'))
AND
(Date, Time) <= (DATE('2012-04-17'), TIME('08:00:00'))
Compound index on (Date, Time) would help in both yours and the above version.
If you have several of those conditions, like your example:
SELECT <select statements>
FROM <from statements>
WHERE
(
(
(Date = '2012-04-16' AND Time >= '20:00:00')
OR
(Date = '2012-04-17' AND Time <= '08:00:00')
)
OR
.....
OR
(
(Date = '2012-05-27' AND Time >= '20:00:00')
OR
(Date = '2012-05-28' AND Time <= '08:00:00')
)
)
you could turn it into:
SELECT <select statements>
FROM <from statements>
CROSS JOIN
( SELECT TIME('20:00:00') AS start_time
, TIME('08:00:00') AS end_time
) AS cc
JOIN
( SELECT d AS this_day
, d + INTERVAL 1 DAY AS next_day
FROM
( SELECT DATE('2012-04-16') AS d
UNION ALL
...
UNION ALL
SELECT '2012-05-27'
) AS s
) AS selected
ON (Date, Time) >= (selected.this_day, cc.start_time)
AND (Date, Time) <= (selected.next_day, cc.end_time )
I've recently came across such issue, where I've to write a code for three shifts including night shift, which involves current day and next day as well.
here is the code:
Select *, Case When Shifts = 'Night' and
(DATEPART(HOUR,DATEADD(day,1,SystemDate_PST))>='00' //next day data//
and DATEPART(HOUR,DATEADD(day,1,SystemDate_PST)) <='06')
THEN DATEADD(DAY,-1, SystemDate_PST) else SystemDate_PST // to show next day data as current day data//
end as Timings from
(
Select *,
case
when DATEPART(HOUR, SystemDate_PST)*60+DATEPART(MINUTE, SystemDate_PST) between
06*60+30 and 14*60+30 then 'Morning'
When DATEPART(HOUR, SystemDate_PST)*60+DATEPART(MINUTE, SystemDate_PST) between
23*60-30 and 23*60+60 then 'Night'
when datepart(HOUR,dateadd(day,1,SystemDate_PST)) >='00'
and datepart(HOUR,dateadd(day,1,SystemDate_PST))*60 + DATEPART(MINUTE, SystemDate_PST
<=07*60-30 then 'Night'
else 'Noon'
end as Shifts from (Select * from Table) a )b
My shift timings are:
Morning: 06:30AM - 14:29PM
Noon: 14:30PM - 22:29PM
Night : 22:30PM- 06:29AM