Daan Vermeulen
Daan Vermeulen

Reputation: 87

Calculating non-overlapped total duration of begin-end datetimes

I have a database table with a Begindate (datetime) and Enddate (datetime). This table contains activity durations. Now I am trying to create a function to calculate the total duration based on non-overlapping activities within a specified begindate-enddate.

An exaple would be as follow:

I am calling upon this function giving the parameters: Begindate='2012-08-16 10:00' Enddate='2012-08-16 18:00'

Now let's say the following data is in the table:

  1. begin: '2012-08-15 10:00' end '2012-08-15 14:00' (total 4 hours)
  2. begin: '2012-08-16 09:00' end '2012-08-16 11:00' (total 2 hours)
  3. begin: '2012-08-16 10:30' end '2012-08-16 10:45' (total 15 minutes)
  4. begin: '2012-08-16 12:00' end '2012-08-16 16:00' (total 4 hours)
  5. begin: '2012-08-16 13:00' end '2012-08-16 17:00' (total 4 hours)
  6. begin: '2012-08-16 16:30' end '2012-08-16 16:45' (total 15 minutes)
  7. begin: '2012-08-16 17:30' end '2012-08-16 20:00' (total 2 hours and 30 minutes)

Now I would love the following to happen:

  1. The first activity is outside the specified period so will not be included.
  2. Partial match so this will return 1 hour.
  3. Activity is inside the second activity so will not be included.
  4. 4 hours withing the period will be included.
  5. 1 hour will be included from 16:00 to 17:00
  6. Activity will not be included.
  7. Only 30 minutes are inside the specified period so that will be included.

The total returned will be: 6 hours and 30 minutes.

If anyone could help me with this I would be very grateful! :)

Upvotes: 1

Views: 404

Answers (1)

Nikola Markovinović
Nikola Markovinović

Reputation: 19356

It might be easier to calculate missing ranges and then substract durations from complete period:

declare @begindate datetime = '20120816 10:00:00'
declare @enddate datetime = '20120816 18:00:00'

; with supplement as (
  select begindate, enddate
    from daterangetable
      -- overlapping ranges only
   where begindate <= @enddate
     and enddate >= @begindate
  union all
  -- empty start range to catch missing start intervals
  select @begindate, @begindate
  union all
  -- empty end range to catch missing end intervals
  select @enddate, @enddate
)
select datediff (minute, @begindate, @enddate)
     - sum (datediff (minute, gapStart, gapEnd)) Minutes
from
(
  select max(d2.enddate) gapStart, 
         d1.begindate gapEnd
    from supplement d1
   cross join supplement d2
      -- d2 range must start before d1 range
   where d2.begindate < d1.begindate
   group by d1.begindate
      -- Range is completely covered if d2 ends after d1 begins
      -- so it should be removed
  having max(d2.enddate) < d1.begindate
) missingRanges

Two ranges overlap if end of first one is after start of second one and start of first one is before end of second one. See the link for (much better) explanation.

If you are not using Sql Server 2005 or newer, you will need to move code inside with into derived tables for supplement d1 and supplement d2.

And here is Sql Fiddle with example.

Upvotes: 3

Related Questions