We help IT Professionals succeed at work.

SQL date check issue

Andy Green
Andy Green asked
on
142 Views
Last Modified: 2014-12-12
Hi

I'm having one of those days (had one yesterday too)

I have a booking system and bookings have a slotStart and slotEnd datetime.

When making a booking I need to check if it overlaps with an existing one. I have all the logic and returns values done, what I can't sort out is the sql.

I have a table with slotstart and slot end, and I pass in the same (Start/ End) I want to return 1 if the new booking overlaps an existing.

I have this but the where isn't right:
      Select  1
      From RoomBookSchedule
      Where Location_ID = @Location_ID
      And isnull(IsDeleted,0) = 0
      And
      (SlotStart < @SlotStart Or SlotStart <= @SlotEnd)

Andy
Comment
Watch Question

Vitor MontalvãoIT Engineer
CERTIFIED EXPERT
Distinguished Expert 2017

Commented:
What are you getting? An error?
Can you provide a sample of data so we can work it out?

Author

Commented:
Not getting error, just getting data I don't want.

A
Vitor MontalvãoIT Engineer
CERTIFIED EXPERT
Distinguished Expert 2017

Commented:
The SELECT only returns '1'. How do you know if the data is what you want?

Author

Commented:
create table #RoomBookSchedule
(      Location_ID int
,      SlotStart smalldatetime
,      SlotEnd smalldatetime);


Insert into #RoomBookSchedule
values            (2, '2014-12-12 08:30:00', '2014-12-12 09:30:00')
            ,      (2, '2014-12-12 09:30:00', '2014-12-12 10:30:00')
            ,      (2, '2014-12-12 12:30:00', '2014-12-12 13:30:00')
            ,      (2, '2014-12-12 13:30:00', '2014-12-12 14:30:00')
            
            
            
declare @Location_ID int
declare @SlotStart smalldatetime
declare @SlotEnd smalldatetime
declare @Clinic_ID int

set @Location_ID = 2
set @SlotStart ='2014-12-12 10:30:00'
set @SlotEnd ='2014-12-12 11:30:00'
set @Clinic_ID = 0


      Select  1
      From RoomBookSchedule
      Where Location_ID = @Location_ID
      And isnull(IsDeleted,0) = 0
      And
      (SlotStart < @SlotStart Or SlotStart <= @SlotEnd)


drop table #RoomBookSchedule


If you look at the data there is a slot between 1030 & 1230, so the proc should return nothing as there isn't a slot at 1030 - 1130.

It should return 1 if the parameters overlap any existing booking.

A

Author

Commented:
The return 1 is a flag to be used on the ASP page. If it returns 1 there is an overlap with existing booking and this is echoed on the asp.net page

A
ste5anSenior Developer
CERTIFIED EXPERT

Commented:
What results do you expect?

AND  (SlotStart < @SlotStart Or SlotStart <= @SlotEnd) means SlotStart < MAXIMUMOF(@SlotStart, @SlotEnd).
For a range check you should use AND  (SlotStart > @SlotStart Or SlotStart <= @SlotEnd). Wrong comparision operator, I guess.
Vitor MontalvãoIT Engineer
CERTIFIED EXPERT
Distinguished Expert 2017

Commented:
Then your criteria is not correct. Should be like this:
Select  1
From RoomBookSchedule
Where Location_ID = @Location_ID 
   And isnull(IsDeleted,0) = 0 
   And (SlotStart > @SlotStart And SlotStart <= @SlotEnd)

Open in new window

Author

Commented:
Not quite right, if you change the parameter to set @SlotStart ='2014-12-12 10:29:00', this ovrlaps an existing booking by 1 min, so should return 1
Koen Van WielinkBusiness Intelligence Specialist

Commented:
I think what you're trying to achieve cannot be done in this way. I understand your logic and I don't think the comparison operators were wrong. What you were trying to check is if there is a line which has a start time before the desired start time @slotStart and if so, make sure that it also ends before the desired start time. While this might apply to the second line of your sample data (the one before the empty slot), it does NOT apply to your first line of dummy data. As such, the query returns 1.
You're basically looking for a line in the table that does not yet exist. Therefore your query always returns 1, because it has overlap with all the records that DO exist.
I'm still trying to work out a way that does give you what you want, but it's a bit tricky. Will let you know when I crack it.

Author

Commented:
Thanks Koen. If it helps I do a search for empty slots, maybe I should be doing this the same way.

I have a function that returns a table of slots:

ALTER Function [dbo].[CreateTimeSlots](

@SlotStart smalldatetime,
@SlotEnd smalldatetime

)
Returns

@Slots TABLE
(
SlotStart smalldatetime,
SlotEnd smalldatetime
)

As
Begin

;With cteSlots (SlotStart, SlotEnd) AS (
    select @SlotStart,DATEADD(n, 30, @SlotStart)

    Union ALL

    select
      DATEADD(n, 30, SlotStart),DATEADD(n, 60, SlotStart)

    From cteSlots
    Where SlotStart <= DATEADD(n, 30, @SlotEnd)
      )

      Insert Into @Slots (SlotStart, SlotEnd)
      Select SlotStart,SlotEnd
      From cteSlots
      where SlotStart >= @SlotStart And SlotEnd <= @SlotEnd
      Option(Maxrecursion 3660);
      Return

End

I then cursor though these slots populating a temp table if a slot is occupied.

Andy
Vitor MontalvãoIT Engineer
CERTIFIED EXPERT
Distinguished Expert 2017

Commented:
Yes, you need to search for gaps and compare those gaps with your variables.
ste5anSenior Developer
CERTIFIED EXPERT

Commented:
E.g. when a row exists, then you can insert it.

-- Assumption: There are no overlapping rows in your table.
DECLARE @RoomBookSchedule TABLE
    (
      SlotStart SMALLDATETIME ,
      SlotEnd SMALLDATETIME
    );

INSERT  INTO @RoomBookSchedule
VALUES  ( '20141212 08:30:00', '20141212 09:30:00' ),
        ( '20141212 09:30:00', '20141212 10:30:00' ),
        ( '20141212 12:30:00', '20141212 13:30:00' ),
        ( '20141212 13:30:00', '20141212 14:30:00' );             

DECLARE @SlotStart SMALLDATETIME = '20141212 10:30:00';
DECLARE @SlotEnd SMALLDATETIME = '20141212 11:30:00';

-- @SlotStart and @SlotEnd must be in the same gap.
WITH    Data
          AS ( SELECT   RBS.SlotStart ,
                        RBS.SlotEnd ,
                        LAG(RBS.SlotEnd, 1, '19000101') OVER ( ORDER BY RBS.SlotStart ) AS PreviousSlotEnd
               FROM     @RoomBookSchedule RBS
               UNION ALL
               SELECT   '20790606' ,
                        '20790606' ,
                        MAX(RBS.SlotEnd)
               FROM     @RoomBookSchedule RBS
             )
    SELECT  *
    FROM    Data D
    WHERE   @SlotStart BETWEEN D.PreviousSlotEnd AND D.SlotStart
            AND @SlotEnd BETWEEN D.PreviousSlotEnd AND D.SlotStart;

Open in new window

Business Intelligence Specialist
Commented:
This one is on us!
(Get your first solution completely free - no credit card required)
UNLOCK SOLUTION
ste5anSenior Developer
CERTIFIED EXPERT

Commented:
Or
-- Without LAG()
WITH    Data
          AS ( SELECT   RBS.SlotStart ,
                        RBS.SlotEnd ,
                        COALESCE(( SELECT   MAX(I.SlotEnd)
                                   FROM     @RoomBookSchedule I
                                   WHERE    I.SlotEnd <= RBS.SlotStart
                                 ), '19000101') AS PreviousSlotEnd
               FROM     @RoomBookSchedule RBS
               UNION ALL
               SELECT   '20790606' ,
                        '20790606' ,
                        MAX(RBS.SlotEnd)
               FROM     @RoomBookSchedule RBS
             )
    SELECT  *
    FROM    Data D
    WHERE   @SlotStart BETWEEN D.PreviousSlotEnd AND D.SlotStart
            AND @SlotEnd BETWEEN D.PreviousSlotEnd AND D.SlotStart;

Open in new window

.
ste5anSenior Developer
CERTIFIED EXPERT

Commented:
btw, when your slots have a constant length, then it's simple integer math. Just project the time to an int..
 
-- Assuming that a slot is always 1h and start always at hh:30:00.
-- Slot available, when no row found.
WITH    Data
          AS ( SELECT   CAST(RBS.SlotStart AS DATE) AS DateDay ,
                        DATEDIFF(MINUTE, '00:30:00', CAST(RBS.SlotStart AS TIME)) / 60 AS SlotNo
               FROM     @RoomBookSchedule RBS
             )
    SELECT  *
    FROM    Data D
    WHERE   D.DateDay = CAST(@SlotStart AS DATE)
            AND D.SlotNo = DATEDIFF(MINUTE, '00:30:00', CAST(@SlotStart AS TIME)) / 60;

Open in new window

Author

Commented:
Thanks Guys, Sorry should have said SQL 2012 so cant use LAG(). Koens solution worked.
ste5anSenior Developer
CERTIFIED EXPERT

Commented:
You don't need LAG(). It makes it only easier to read. See my second solution. No temp table required.

btw, seems like an important information. Please add this in future posts.

Author

Commented:
I did see your second solutions Ste5an, but I couldn't get them to work, my accepted solution work first time.

I will make sure the sql version is in future posts.

Andy
Unlock the solution to this question.
Join our community and discover your potential

Experts Exchange is the only place where you can interact directly with leading experts in the technology field. Become a member today and access the collective knowledge of thousands of technology experts.

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.