Marcus
Marcus

Reputation: 9439

Sum a date range inside another date range

Trying to write a query in SQL for the following: an event has a start/end date/time. Users are available for certain times. How can I find the total time a user is available for a particular event?

Example:

--Event--
eventID eventStart          eventEnd
1       2012-10-10 15:00    2012-10-10 18:00

--Available--
userID  availStart          availEnd
64      2012-10-10 10:00    2012-10-10 16:00
64      2012-10-10 16:30    2012-10-10 16:40
64      2012-10-10 16:55    2012-10-10 22:00

The user is free for 135 minutes (60 mins from 15:00-16:00, 10 mins from 16:30-16:40, 65 mins from 16:55-18:00).

Some help writing the SQL would be really helpful, finding this one tricky!

Upvotes: 1

Views: 263

Answers (1)

Laurence
Laurence

Reputation: 10976

Tested it now: http://sqlfiddle.com/#!3/a4e7a/2

I've assumed a user table. There's lots of scope for performance improvement. E.g by adding a where clause that eliminates ranges that don't overlap. It'll probably also be faster if you replace the function with a hideous case statement.

The tricky bit is figuring out the algorithm for how much of two timespans overlap. I always find it useful to draw pictures of the cases:

Case 1
|------|
         |=======|

Case 2
|------|
     |======|

Case 3
|-------|
  |===|

and the equivalent with the orders reversed.

It turns out the overlap is the minimum of the two end times minus the maximum of the two start times. (If negative there is no overlap). I always have to check all the cases to re-convince myself of this.

-- Function that determines how many minutes of overlap there are between two timespans
Create Function dbo.MinutesOverlap(
  @Start1 as datetime, @End1 as datetime, @Start2 as datetime, @End2 as datetime
) Returns int As
Begin
  Declare 
    @MaxStart As datetime,
    @MinEnd As datetime,
    @Ret int = 0

  Set @MaxStart = Case When @Start1 > @Start2 Then @Start1 Else @Start2 End
  Set @MinEnd = Case When @End1 > @End2 Then @End2 Else @End1 End

  If @MaxStart < @MinEnd
    Set @Ret = DateDiff(Minute, @MaxStart, @MinEnd)

  Return @Ret
End


Select
  u.UserID,
  e.EventID,
  Sum(dbo.MinutesOverlap(e.eventStart, e.eventEnd, a.availStart, a.availEnd))
From
  Event e
    Cross Join
  User u
    Left Outer Join
  Available a
    On u.UserID = a.UserID
Group By
  u.UserID, 
  e.EventID

Upvotes: 1

Related Questions