Поиск пробелов в метках времени для нескольких пользователей и перекрывающихся интервалов времени в PostgreSQL - PullRequest
1 голос
/ 10 февраля 2020

Это продолжение предыдущего поста на этом сайте: Поиск пробелов в метках времени для нескольких пользователей в PostgreSQL

Я работаю с набором данных, содержащим Check-In и Check - Время работы нескольких офисных помещений за последние 5 лет. Один из проектов, над которым меня попросили поработать, - это подсчет времени, в течение которого каждая комната занята и свободна в различных временных интервалах (ежедневно, еженедельно, ежемесячно и т. Д. c.), Исходя из установленных рабочих часов (с 7:30 до 17:00). , Отклоняясь от моего последнего поста, бывают случаи перекрытия временных диапазонов. Пример набора данных за один день выглядит следующим образом:

room_id     check_in                check_out
"Room D"    "2014-07-18 12:23:00"   "2014-07-18 12:54:00"
"Room D"    "2014-07-19 09:16:00"   "2014-07-19 10:30:00"
"Room D"    "2014-07-19 09:10:00"   "2014-07-19 10:30:00"
"Room D"    "2014-07-18 08:45:00"   "2014-07-18 22:40:00"
"Room 5"    "2014-07-19 10:20:00"   "2014-07-19 12:20:00"
"Room 5"    "2014-07-18 07:59:00"   "2014-07-18 09:00:00"
"Room 5"    "2014-07-18 09:04:00"   "2014-07-18 14:00:00"
"Room 5"    "2014-07-18 07:59:00"   "2014-07-18 10:00:00"

Из моего предыдущего поста мне очень помог этот фрагмент кода, который отлично работает для всех случаев, когда нет перекрытия , как указывает автор:

select date_trunc('day', start_dt), room_id,
       sum( least(extract(epoch from end_dt), v.epoch2) - 
            greatest(extract(epoch from start_dt), epoch1)
          ) as busy_seconds,
       (epoch2 - epoch1 -
        sum( least(extract(epoch from end_dt), v.epoch2) - 
             greatest(extract(epoch from start_dt), epoch1)
           )
       ) as free_seconds
from rooms r cross join
     (values (extract(epoch from date_trunc('day', start_dt) + interval '7 hours 30 minutes'),
              extract(epoch from date_trunc('day', start_dt) + interval '17 hour')
             )
     ) v(epoch1, epoch2)                  
group by date_trunc('day', start_dt), room_id

Однако, после того, как мы покопаемся в наших данных, количество перекрывающихся временных интервалов увеличивается, чем я ожидал. Вот целевой вывод, который я хотел бы получить из приведенных выше примеров:

target_day      room_id         busy_time         Free Time
2014-07-18      Room D          8.25              1.25
2014-07-19      Room 4          1.33              8.17
2014-07-18      Room 5          8                 1.5
2014-07-19      Room 5          2                 7.5

Я сейчас изучаю PostgreSQL, так что эта проблема немного над моей головой. Буду очень признателен за любую помощь или руководство!

1 Ответ

1 голос
/ 10 февраля 2020

Для обработки пробелов я бы рекомендовал сначала их объединить - скажем, с помощью CTE. Далее следует логика c по:

  • Просмотр максимальной даты окончания до данной строки (для той же комнаты и того же времени.
  • Делать кумулятивную сумму, где есть разрыв между предыдущей максимальной датой окончания и датой начала.
  • Использование этого для агрегирования по room_id для расчета нового времени начала и окончания.

Это должно работать, но вы можете проверить CTE перед применением logi c в вашем другом запросе (единственное изменение относится к CTE, а не к базовой таблице).

Как запрос:

with r as (
      select room_id, min(start_dt) as start_dt, max(end_dt) as end_ddt
      from (select r.*,
                   count(*) over (filter where prev_end_dt < start_dt) over (partition by room_id date_trunc('day', start_dt) order by start_dt) as grp
            from (select r.*,
                         max(end_dt) over (partition by room_id, date_trunc('day', start_dt) rows between unbounded preceding and 1 preceding) as prev_end_dt
                  from rooms r
                 ) r
           ) r
      group by room_id, grp
     )
select date_trunc('day', start_dt), room_id,
       sum( least(extract(epoch from end_dt), v.epoch2) - 
            greatest(extract(epoch from start_dt), epoch1)
          ) as busy_seconds,
       (epoch2 - epoch1 -
        sum( least(extract(epoch from end_dt), v.epoch2) - 
             greatest(extract(epoch from start_dt), epoch1)
           )
       ) as free_seconds
from r cross join
     (values (extract(epoch from date_trunc('day', start_dt) + interval '7 hours 30 minutes'),
              extract(epoch from date_trunc('day', start_dt) + interval '17 hour')
             )
     ) v(epoch1, epoch2)                  
group by date_trunc('day', start_dt), room_id
...