Разделение времени на часовые интервалы в Oracle (CTE) - PullRequest
0 голосов
/ 10 января 2019

Итак, моя цель состоит в том, чтобы иметь возможность подсчитывать время, затрачиваемое на определенные виды деятельности, в диапазоне часов.

Мои данные содержат: начало определенного действия и конец этого действия, например, я знаю, что у кого-то был перерыв с «2019-01-09 17:04:34» на «2019-01-09 19:55:03».

Моя цель - рассчитать, что этот человек потратил 55 минут на перерыв в интервале «17-18», 60 минут на «18-19» и 55 минут на «19-20».

Моя идея состояла в том, чтобы всегда разбивать источник, чтобы для строки, содержащей начало, и для действия я получал столько строк, сколько разделил мой временной диапазон по часам (для этого примера данных я получил бы 3: строки с ' 2019-01-09 17:04:34 'до' 2019-01-09 17:59:59 ',' 2019-01-09 18:00:00 'до' 2019-01-09 18:59:59 ' и с «2019-01-09 19:00:00» до «2019-01-09 19:55:03»)

Если бы я мог получить что-то подобное, мне удалось бы посчитать все, что мне нужно. Я предсказываю, что для получения этого результата я должен использовать CTE (поскольку мы не знаем, во сколько диапазонах нам нужно разделить временной интервал), но у меня нет опыта в этом.

Надеюсь, мне удалось ясно объяснить мою проблему. Я работаю над разработчиком Oracle SQL.

Буду очень признателен за вашу помощь, хотя бы по нескольким советам.

Ответы [ 2 ]

0 голосов
/ 11 января 2019

Из вашего поста не совсем понятно, как вы хотите обрабатывать компоненты с ненулевыми секундами (какая комбинация округления и / или усечения). В любом случае, это можно легко кодировать, если согласован полный набор непротиворечивых правил.

Кроме этого, ваш вопрос состоит из двух частей: укажите правильные часы для каждого идентификатора (каждого действия или события) и продолжительность части этого события в течение этого часа. В приведенном ниже запросе, используя иерархическую технику CONNECT BY, я генерирую часы и продолжительность в виде интервала от дня до секунды. Как я уже сказал, это можно преобразовать в минуты (от 0 до 60), как только вы уточните правила округления.

with
  your_table (id, start_time, end_time) as (
    select 1, timestamp '2019-01-09 17:04:34', timestamp '2019-01-09 19:55:03' 
    from dual union all
    select 2, timestamp '2019-01-09 23:47:01', timestamp '2019-01-10 02:05:03'
    from dual union all
    select 3, timestamp '2019-01-09 18:01:01', timestamp '2019-01-09 18:02:07'
    from dual union all
    select 4, timestamp '2019-01-09 13:00:00', timestamp '2019-01-09 14:00:01'
    from dual
  )
select id,
       trunc(start_time, 'hh') + interval '1' hour * (level - 1) as hr,
       case when level = 1 and connect_by_isleaf = 1 
              then end_time - start_time
            when level = 1                           
              then trunc(start_time, 'hh') + interval '1' hour - start_time
            when connect_by_isleaf = 1 
              then end_time - trunc(end_time, 'hh')
            else interval '1' hour
       end  as duration
from   your_table
connect by trunc(start_time, 'hh') + interval '1' hour * (level - 1) < end_time
       and prior id = id
       and prior sys_guid() is not null
;

выход

        ID HR                  DURATION           
---------- ------------------- -------------------
         1 2019-01-09 17:00:00 +00 00:55:26.000000
         1 2019-01-09 18:00:00 +00 01:00:00.000000
         1 2019-01-09 19:00:00 +00 00:55:03.000000
         2 2019-01-09 23:00:00 +00 00:12:59.000000
         2 2019-01-10 00:00:00 +00 01:00:00.000000
         2 2019-01-10 01:00:00 +00 01:00:00.000000
         2 2019-01-10 02:00:00 +00 00:05:03.000000
         3 2019-01-09 18:00:00 +00 00:01:06.000000
         4 2019-01-09 13:00:00 +00 01:00:00.000000
         4 2019-01-09 14:00:00 +00 00:00:01.000000
0 голосов
/ 10 января 2019

Поскольку вы упомянули рекурсию, здесь используется рекурсивный факторинг подзапросов:

-- CTE for sample data
with your_table (id, start_time, end_time) as (
  select 1, timestamp '2019-01-09 17:04:34', timestamp '2019-01-09 19:55:03' from dual
  union all
  select 2, timestamp '2019-01-09 23:47:01', timestamp '2019-01-10 02:05:03' from dual
  union all
  select 3, timestamp '2019-01-09 18:01:01', timestamp '2019-01-09 18:02:07' from dual
  union all
  select 4, timestamp '2019-01-09 13:00:00', timestamp '2019-01-09 14:00:01' from dual
),
-- recursive CTE
rcte (id, hour_period, minutes, period_start_time, end_time, hour_num) as (
  select id,
    -- first period is the original start hour
    extract(hour from start_time),
    -- minutes in first period, which can end at the end of that hour, or at original
    -- end time if earlier
    case when extract(minute from end_time) = 0
         and end_time >= cast(trunc(start_time, 'HH') as timestamp) + interval '1' hour
         then 60
         else extract(minute from
           least(cast(trunc(start_time, 'HH') as timestamp) + interval '1' hour, end_time)
           - start_time
         )
    end,
    -- calculate next period start
    cast(trunc(start_time, 'HH') as timestamp) + interval '1' hour,
    -- original end time
    end_time,
    -- first hour period (for later ordering)
    1
  from your_table
  union all
  select id,
    -- this period's hour value
    extract(hour from period_start_time),
    -- minutes in this period - either 60 if we haven't reach the end time yet;
    -- or if we have then the number of minutes from the end time
    case when end_time < period_start_time + interval '1' hour
         then extract(minute from end_time)
         else 60
    end,
    -- calculate next period start
    period_start_time + interval '1' hour,
    -- original end time
    end_time,
    -- increment hour period (for later ordering)
    hour_num + 1
  from rcte
  where period_start_time < end_time
)
select id, hour_period, minutes
from rcte
order by id, hour_num;
        ID HOUR_PERIOD    MINUTES
---------- ----------- ----------
         1          17         55
         1          18         60
         1          19         55
         2          23         12
         2           0         60
         2           1         60
         2           2          5
         3          18          1
         4          13         60
         4          14          0

Функция поиска находит количество времени, проведенного в первом часе периода в элементе привязки, затем рекурсивно просматривает последующие часы до достижения конечного времени, увеличивая время окончания переданного периода каждый раз; и в рекурсивном члене он проверяет, использовать ли фиксированные 60 минут (если он знает, что время окончания не было достигнуто) или использовать фактические минуты с момента окончания.

Мои примеры периодов включают периоды, которые охватывают полночь, охватывают менее часа и начинаются в первую минуту часа - и заканчиваются в первой минуте часа, которая (в любом случае, по моим расчетам) заканчивается строка для этого часа в любом случае и количество минут, как ноль. Вы можете легко отфильтровать это, если не хотите видеть это.

...