How do I compare overlapping values within a row? - sql

I seem to have a problem with this SQL query:
SELECT * FROM appts
WHERE timeStart >='$timeStart'
AND timeEnd <='$timeEnd'
AND dayappt='$boatdate'
The time is formatted as military time. The logistics is that a boat rental can be reserved at 7am til 1pm or 9am til 1pm or 9am til 5pm. If there is an appt within that range, it should return appts but it has proven to be inconsistent. If I pick 9am til 1pm, it will ignore appts that started with 7am even though it overlaps 9am-1pm. If I pick 9 to 5, it will return nothing even though it should with the 7am to 1pm. How do I make a SQL statement that includes the whole range from timeStart to timeEnd including those that overlap?

Shahkalpesh answered the question with:
I think you need an OR.
SELECT * FROM appts
WHERE (timeStart >='$timeStart'
OR timeEnd <='$timeEnd')
AND dayappt='$boatdate'
I posted a comment that I consider this to be wrong, giving a pair of counter-examples:
This is plain wrong - #ShaneD is correct. For example, this will pick out a booking between 05:00 and 06:00 because the actual end time is less than any of the end times you ask about. It will also pick up rentals from 18:00 onwards, for the equivalent reason.
In a response to my comment, Shahkalpesh requested:
Could you post a separate reply with data & input parameters with expected output?
Fair enough - yes. Slightly edited, the question says:
The logic is that a boat rental can be reserved
from 7am until 1pm, or
from 9am until 1pm, or
from 9am until 5pm.
If there is an appointment within that range, it should return appointments but it has proven to be inconsistent. If I pick 9am until 1pm, ...
Enough background. We can ignore the date of the appointments, and just consider the times. I'm assuming that there is an easy way to limit the times recorded to hh:mm format; not all DBMS actually provide that, but the extension to handle hh:mm:ss is trivial.
Appointments
Row timeStart timeEnd Note
1 07:00 13:00 First valid range
2 09:00 13:00 Second valid range
3 09:00 17:00 Third valid range
4 14:00 17:00 First plausibly valid range
5 05:00 06:00 First probably invalid range
6 18:00 22:30 Second probably invalid range
Given a search for appointments overlapping the range 09:00 - 13:00, Shahkalpesh's (simplified) query becomes:
SELECT * FROM Appointments
WHERE (timeStart >= '09:00' OR timeEnd <= '13:00')
This will return all six rows of data. However, only rows 1, 2, 3 overlap the time period 09:00 - 13:00. If rows 1, 2, and 3 are the only valid representative appointment values, then Shahkalpesh's query produces the correct answer. However, if the row 4 (which I think is plausibly valid) is permitted, then it should not be returned. Similarly, rows 5 and 6 - if present - should not be returned. [Actually, assuming timeStart <= timeEnd for all rows in the table (and there are no NULL values to mess things up), we can see that Shahkalpesh's query will return ANY row of data for the 09:00-13:00 query because either the start time of the row is greater 09:00 or the end time is less than 13:00 or both. This is tantamount to writing 1 = 1 or any other tautology in the WHERE clause.]
If we consider ShaneD's query (as simplified):
SELECT * FROM Appointments
WHERE timeStart <= '13:00' AND timeEnd >= '09:00'
we see that it also selects rows 1, 2, and 3, but it rejects rows 4 (because timeStart > '13:00'), 5 (because timeEnd < '09:00') and 6 (because timeStart > '13:00'). This expression is an archetypal example of how to select rows which 'overlap', counting 'meets' and 'met by' (see "Allen's Interval Algebra", for instance) as overlapping. Changing '>=' and '<=' alters the set of intervals counted as overlapping.

The correct check would look like this:
SELECT * FROM appts
WHERE timeStart <='$timeEnd'
AND timeEnd >='$timeStart'
AND dayappt='$boatdate'
Other good explanations have been given but I'll go ahead and update it with an alternative explanation of how I visualize this myself. Most people are looking for each possible overlap, considering two time periods, they are trying to think of each combination of start and end that can make an appointment overlap. I think about it as when do two time periods not overlap which for some reason is easier for me.
Say the time period I am checking for is today, I want to find any time period that does not overlap today. There are really only two scenarios for that, either the time period starts after today (PeriodStart > EndOfToday) or the time period ends before today (PeriodEnd < StartOfToday).
Given that we havea simple test for not overlapping:
(PeriodStart > EndOfToday) OR (PeriodEnd < StartOfToday)
A quick flip around and you have a simple test for overlap:
(PeriodStart <= EndOfToday) AND (PeriodEnd >= StartOfToday)
-Shane

Thanks Shane, Shahkalpesh, and Jonathan.
I actually overlooked the fact that Shane "swapped" the variables (I was still using timeStart<=$timeStart when it should be timeStart <= $timeEnd). I ran with the modified statement as Jonathan/Shane suggested and it works. As Jonathan did point out, I did obviously missed out some time ranges that I should have tested against.
Now with Jonathan's explanation, I now get a better picture of my mistake is and it's helpful.

I think you need an OR.
SELECT * FROM appts
WHERE (timeStart >='$timeStart'
OR timeEnd &LT;='$timeEnd')
AND dayappt='$boatdate'
Assuming each record cares about only a specific day.
i.e. Boats rented don't run across more than 1 day.

Related

how to write a sql to calculate working hours minus rest time

I have a table of rest time in work shift
Begin end
12:00 12:30
17:30 18:30
Now I want to write a SQL to calculate actual working hours given the start and end time. For example if start at 9:00 and end at 15:00, the actual hours is 6-rest time=5.5 hours and if start at 9:00 and end at 20:00 the actual hours is 10 hours. How to write a procedure to check it in SQL server? Thx.
There are no schema details to work with here, which means the following SQL is generic and will have to be altered to fit your db.
SELECT
(datediff(minute, shiftStartTime, shiftEndTime)
- datediff(minute,breakStartTime,breakEndTime)) / 60.0
FROM yourTable
Notes:
If they can have multiple breaks, you need to sum up all the break times in minutes before deducting it from the shift period.
the calculation is specifically in minutes because the datediff counts the number of boundaries passed, so the date diff in hours between 11:59 and 12:01 is 1, even though the break is 2 minutes, you would count that as 1 hour if you count hours using the function.
If you can provide more schema details, we would be able to craft a more complete statement.
you can try below way using DATEDIFF
select *, CONVERT(time(7),DATEADD(s, DATEDIFF(s,S,E),'00:00:00')) from QQ
http://sqlfiddle.com/#!18/01213d/1
for your case column name will be
select *, CONVERT(time(7),DATEADD(s, DATEDIFF(s,Begin,end),'00:00:00')) from yourtable

Query to find a block of time in a schedule

Imagine I had a work schedule from 9am to 6pm. It is divided into 15 minute blocks and appointments (increments of 15 minutes) can be fitted into the times available.
Now, if I need to insert a new appointment that is 45 minutes long is there an easy query to find a block of time that is available to fit the appointment in for a given date
The basic table design is
AppointmentId
Date
StartTime
Length - 15 minute incremenents
I would like to get a list of available times to choose from, so if the only appointment for the given day is a 30 minute one at 9:30 then the list of times would be
(No times before 9:30 as the 45 minute appointment wont fit)
10:15
10:30
10:45
...
5:15pm (last time of the day the appointment will fit)
By using ranking function (i.e Row_Number()) set number for each row in each day (let say it's name is rn), then join this query with it self by this condition q2.rn = q1.rn-1 then you have end of appointment beside start of next appointment, then calculate datediff(mi) on this end and start, so this value is the gap, then write another query wrapping this query to filter records that have gap >= yourNeededTime. Also for start of day and end of day you can create 2 dummy records one for 9am and one for 6pm so that yo can handle gap of start of day to the first appointment and last appointment to the end of day.
I hope this helps

DATEDIFF - How many days until Sunday

In SQL Server, trying to write a age-off report for inventory purposes. Each week, the inventory system marks thousands of rows for deletion. This takes place on Sundays # 06:00:00 as part of weekly SQL DB purge schedule.
Using (yyyy-mm-dd hh:mm:ss:ms) format for closed_time, how can I calculate the numbers of days between that date, until next Sunday of the current week? And to be more elaborate, is there a way to narrow it down to the exact DD:HH:MM? The problem is the each client's Sunday DB schedule for purge varies. So that might be difficult to compute. Might be easier to just calculate whole days until Sunday 00:00:00. I tried using the DATEDIFF function with no success.
SELECT
Yada
DATEDIFF(DAY, closed_time,DW) AS Days_Until_Purged
FROM DB1
WHERE closed_time DESC
Thx in advance
If you choose any Sunday in the past (Such as 06:00 Sunday 2nd January 2000), you can calculate time that has GONE BY since then.
Then, if you take that and do modulo 7-days you get the time that has gone by since the most recent Sunday.
Then, if you do 7 - time_gone_by_since_last_sunday you get the time until the next sunday.
I'm going to do this in minutes to cope with a client that has a setting of 06:30.
DECLARE
#batch_processing_time SMALLDATETIME
SET
#batch_processing_time = '2000-01-02 06:00'
SELECT
(60*24*7) - DATEDIFF(minute, #batch_processing_time, closed_time) % (60*24*7)
FROM
yourTable
That's the number of minutes from each record's closed_time until the next #batch_processing_time.
Divide by (24*60) to get it in days.
try this:
select 8-DATEpart(w, closed_time) AS Days_Until_Purged from DB1 ...
This should solve your problem
SET DATEFIRST 1
Select DATEDIFF(dd,GETDATE(),DATEADD(DAY , 7-DATEPART(WEEKDAY,GETDATE()),GETDATE()))

SQL WHERE timestamp BETWEEN MIDNIGHT AND MIDNIGHT

For my website I must select all messages sent between midnight (the previous night) and midnight (the next night). Basically, it's a 24 hours range.
I don't know how to do that as I store the date in a timestamp format in my DB.For example, last message was posted on 2013-10-18 11:23:35.
What I want is all message posted between 2013-10-18 00:00:00 and 2013-10-18 23:59:59.
Is that possible, if yes, how could I do that ?
You can calculate the required time in T-SQL as :
-- for previous day mid night as start time
declare #start_datetime datetime,#end_datetime datetime
Select #start_datetime = DATEADD(d,0,DATEDIFF(d,0,GETDATE()))
-- for current day mid night as end time
Select #end_datetime = DATEADD(SS,86399,DATEDIFF(d,0,GETDATE()))
select #start_datetime, #end_datetime
and then use you column name to check whether it exists between these two values.
To find out what happened between DatetimeA and DatetimeB, the sql keyword between is not your friend. It generally causes one to miss records. This construct is better.
where YourDateTimeField >= StartDateTime
and YourDateTimeField < JustAfterTheEndDateTime
In your case, you can simplify it with
where YourDateTimeField >= DateA
and YourDateTimeField < TheDayAfterDateA

datetime manipulation: replace all dates with 00:00 time with 24:00 the previous day

I have a table described here: http://sqlfiddle.com/#!3/f8852/3
The date_time field for when the time is 00:00 is wrong. For example:
5/24/2013 00:00
This should really be:
5/23/2013 24:00
So hour 00:00 corresponds to the last hour of the previous day (I didn't create this table but have to work with it). Is there way quick way when I do a select I can replace all dates with 00:00 as the time with 24:00 the previous day? I can do it easily in python in a for loop but not quite sure how to structure it in sql. Appreciate the help.
All datetimes are instants in time, not spans of a finite length, and they can exist in only one day. The instant that represents Midnight is by definition, in the next day, the day in which it is the start of the day, i.e., a day is closed on its beginning and open at its end, or, to phrase it again, valid allowable time values within a single calendar date vary from 00:00:00.00000, to 23:59:59.9999.
This would be analogous to asking that the minute value within an hour be allowed to vary from 1 to 60, instead of from 0 to 59, and that the value of 60 was the last minute of the previous hour.
What you are talking about is only a display issue. Even if you could enter a date as 1 Jan 2013 24:00, (24:00:00 is not a legal time of day) it would be entered as a datetime at the start of the date 2 Jan, not at the end of 1 Jan.
One thing that illustrates this, is to notice that, because of rounding (SQL can only resolve datetimes to within about 300 milleseconds), if you create a datetime that is only a few milleseconds before midnight, it will round up to midnight and move to the next day, as can be seen by running the following in enterprise manager...
Select cast ('1 Jan 2013 23:59:59.999' as datetime)
SQL server stoers all datetimes as two integers, one that represents the number days since 1 Jan 1900, and the other the number of ticks (1 tick is 1/300th of a second, about 3.33 ms), since midnight. If it has been zero time interval since Midnight, it is stll the same day, not the previous day.
If you have been inserting data assuming that midnight 00:00:00 means the end of the day, you need to fix that.
If you need to correct your existing data, you need to add one day to every date in your database that has midnight as it's time component, (i.e., has a zero time component).
Update tbale set
date_time = dateAdd(day, 1, date_time)
Where date_time = dateadd(day, datediff(day, 0, date_time), 0)