简体   繁体   中英

Finding total minutes from overlapping date ranges

I am trying to calculate the total opening time in hours for each hotel. Each day (d1 - d7) the time can be different and a range is given for which that time is applicable. For example row 1 tells us that during the time period 2020-05-15 till 2020-12-31 the hotel is open for 1 hour(9:00 - 10:00) for d2 and for d1 it's not opened.

the issue here is that we have overlapping dates and time. Looking at d2 the shop opens from 9:00 till 10:00 [1 hour] from May 15th till end December 31st. But row 2 shows that from August 27th till December 31st the shop opens for another additional hour (13:00 -14:00pm. So the total hours should be 2 for the range (2020-08-27 to 2020-12-31).

Looking at the data it looks like running total is needed but row 6th shows that it opens for 1 hour but in that case we can't add the previous sum of hours of row 4 and row 5 since their from and till dates are not overlapping with the date range of row 6. But row 1-3 do overlap with row 6 so they should be added.

There are similar cases like these, and there are also cases where we just have 1 row for 1 hotel. Looking at all the cases I am stuck in calculating the hours per d1,d2 for a given time range (from - till)

hotel   d1_from     d1_to       d2_from      d2_to         from        till
    1   00:00:00    00:00:00    09:00:00    10:00:00    2020-05-15  2020-12-31
    1   00:00:00    00:00:00    13:00:00    14:00:00    2020-08-27  2020-12-31
    1   00:00:00    00:00:00    15:00:00    16:00:00    2020-09-11  2020-12-31
    1   09:30:00    10:15:00    18:00:00    19:00:00    2020-11-24  2020-11-25
    1   00:00:00    00:00:00    20:00:00    21:00:00    2020-11-25  2020-11-25
    1   09:30:00    10:15:00    22:00:00    23:00:00    2020-12-01  2020-12-03



select 
  d1_from,
  d1_to,
  d2_from,
  d2_to,
  timediff('minute',d1_from,d1_to) /60 as d1_total,
  timediff('minute',d2_from,d2_to)/60  as d2_total,
  case 
    when from >=  lead(from)over(partition by hotel ORDER by from) 
    then lead(from)over(partition by hotel ORDER by from) 
  else null end as date_adjustment,

  sum(d2_total) over (partition by hotel order by from) as cumulative_d2,
  sum(d1_total) over (partition by hotel order by from) as cumulative_d1


from table





d2_hours (new column)
1 --first row only i.e 9 - 10
2 --first two rows 9- 10 and 13 - 14
3 --first, 2nd and 3rd rows 9-10, 13 - 14 , 15 - 16
4 -- first 4 rows added
5 -- first 5 rows
3 --first 3 rows added only] --> since 2020-12-01 (from) doesn't overlap with the "till" date of the previous row

-- so for each day the logic should be created d1_hours, d2_hours d3_hours etc..

One option is to count the weeks and sum the weekday hours.

WITH CTE AS  
(SELECT 1 HOTEL, '00:00:00' MONDAY_START, '00:00:00' MONDAY_FINISH, '09:00:00'TUESDAY_START, '10:00:00' TUESDAY_FINISH, '2020-05-15'::DATE FROM_DATE, '2020-12-31'::DATE TILL_DATE UNION ALL
 SELECT 1 HOTEL, '00:00:00' MONDAY_START, '00:00:00' MONDAY_FINISH, '13:00:00'TUESDAY_START, '14:00:00' TUESDAY_FINISH, '2020-08-27'::DATE FROM_DATE, '2020-12-31'::DATE TILL_DATE UNION ALL
 SELECT 1 HOTEL, '00:00:00' MONDAY_START, '00:00:00' MONDAY_FINISH, '15:00:00'TUESDAY_START, '16:00:00' TUESDAY_FINISH, '2020-09-11'::DATE FROM_DATE, '2020-12-31'::DATE TILL_DATE UNION ALL
 SELECT 1 HOTEL, '09:30:00' MONDAY_START, '10:15:00' MONDAY_FINISH, '18:00:00'TUESDAY_START, '19:00:00' TUESDAY_FINISH, '2020-11-24'::DATE FROM_DATE, '2020-11-25'::DATE TILL_DATE UNION ALL
 SELECT 1 HOTEL, '00:00:00' MONDAY_START, '00:00:00' MONDAY_FINISH, '20:00:00'TUESDAY_START, '21:00:00' TUESDAY_FINISH, '2020-11-25'::DATE FROM_DATE, '2020-11-25'::DATE TILL_DATE UNION ALL
 SELECT 1 HOTEL, '09:30:00' MONDAY_START, '10:15:00' MONDAY_FINISH, '22:00:00'TUESDAY_START, '23:00:00' TUESDAY_FINISH, '2020-12-01'::DATE FROM_DATE, '2020-12-03'::DATE TILL_DATE )

 SELECT * ,(WEEK(TILL_DATE)-WEEK(FROM_DATE)+1)*(DATEDIFF(HOUR,MONDAY_START::TIME,MONDAY_FINISH::TIME)+DATEDIFF(HOUR,TUESDAY_START::TIME,TUESDAY_FINISH::TIME)) YOUR_ANSWER  FROM CTE   ;

在此处输入图像描述

You can fiddle with day_of_week to get the edge cases just right. However I'll leave that to you as is makes the code somewhat messy and this just to give you an option of how to solve your problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM