Oracle - привязка длительностей к различным 15-минутным интервалам - PullRequest
0 голосов
/ 20 марта 2020

У меня есть база данных, которая предоставляет информацию, когда сотрудник вошел в различные состояния, но проблема в том, что он связывает всю продолжительность с 15-минутным интервалом, независимо от того, длится ли эта продолжительность больше 15-минутного интервала, что вызывает расхождения при создании вычислений эти данные.

Образец набора данных :

**ID    | Date      | Start Time            | End Time              | Secs | State    | SubState**

10001 | 18/MAR/20 | 09:06:41.000000000 AM | 09:09:31.000000000 AM | 170  | Ready    | Ready

10001 | 18/MAR/20 | 09:09:31.000000000 AM | 09:44:41.000000000 AM | 2110 | NotReady | Busy

10001 | 18/MAR/20 | 09:44:41.000000000 AM | 09:51:31.000000000 AM | 410  | NotReady | ACW

10001 | 18/MAR/20 | 09:51:31.000000000 AM | 09:54:25.000000000 AM | 174  | NotReady | Busy

10001 | 18/MAR/20 | 09:54:25.000000000 AM | 09:55:00.000000000 AM | 35   | NotReady | ACW

Желаемый результат :

**ID    | Date      | 15 Int  | Secs  | State    | SubState**

10001 | 18/MAR/20 | 09:00 AM | 170   | Ready    | Ready

10001 | 18/MAR/20 | 09:00 AM | 329   | NotReady | Busy

10001 | 18/MAR/20 | 09:15 AM | 900   | NotReady | Busy

10001 | 18/MAR/20 | 09:30 AM | 881   | NotReady | Busy

10001 | 18/MAR/20 | 09:30 AM | 19    | NotReady | ACW

10001 | 18/MAR/20 | 09:45 AM | 391   | NotReady | ACW

10001 | 18/MAR/20 | 09:45 AM | 174   | NotReady | Busy

10001 | 18/MAR/20 | 09:45 AM | 35    | NotReady | Busy

Какой самый простой способ добиться этого в Oracle SQL?

Ответы [ 2 ]

0 голосов
/ 06 апреля 2020

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

WITH time_intervals AS
(
  SELECT s.min_date+(15*(LEVEL-1)/1440) AS INTERVAL_START,
         s.min_date+(15*LEVEL/1440) AS INTERVAL_END
  FROM (SELECT MIN(TO_DATE(u.event_date, 'DD/MON/YY')) AS MIN_DATE,
               MAX(TO_DATE(u.event_date, 'DD/MON/YY')) AS MAX_DATE
        FROM user_data u) s
  CONNECT BY LEVEL <= (s.max_date-s.min_date+1)*96 --96 is the number of 15 min intervals in a day
)
SELECT u.id AS ID, 
       ti.interval_start, 
       SUM((LEAST(ti.interval_end, u.end_time) - GREATEST(ti.interval_start, u.start_time))*86400) AS SECONDS,
       u.state,
       u.substate
FROM time_intervals ti
INNER JOIN (SELECT ud.id, 
                   TO_DATE(ud.event_date||' '||ud.start_time, 'DD/MON/YY HH:MI:SS PM') AS START_TIME,
                   TO_DATE(ud.event_date||' '||ud.end_time, 'DD/MON/YY HH:MI:SS PM') AS END_TIME,
                   ud.state, ud.substate
            FROM user_data ud) u ON GREATEST(ti.interval_start, u.start_time) < LEAST(ti.interval_end, u.end_time)
GROUP BY u.id, ti.interval_start, u.state, u.substate;

Сначала мы рассчитаем 15-минутные интервалы для дней в вашей таблице. Если вы хотите ограничить свой отчет, просто измените этот CTE на учетную запись только тех дней, которые вы хотите сообщить. Затем мы присоединяем CTE к вашим существующим данным на основе записи в вашей таблице, перекрывающей данный интервал. Каждый перекрывающийся интервал умножается на 86400 (количество секунд в дне), чтобы получить ваш результат. Наконец, мы используем статистическую функцию sum для учета нескольких событий с одним и тем же идентификатором, состоянием и подсостоянием в одном и том же 15-минутном интервале.

Я создал SQLFiddle для проверки ( Link ).

0 голосов
/ 05 апреля 2020

Это похоже на проблему, которую я видел раньше. Пример запроса, который я сделал, основан на взятии каждого 15-минутного временного сегмента и анализе всех интервалов, которые его перекрывают. Попытки сделать это наоборот сбивают с толку.

Я не уверен, основываясь на комментариях, могут ли ваши реальные данные иметь повторяющиеся комбинации состояния / подсостояния на 15-минутный сегмент, и если да, то запрос ниже может потребоваться модификация для правильной агрегации.

Я заметил, что в последнем ряду вашего "Желаемого результата" у вас есть подсостояние "Занято", тогда как в наборе данных этот 35-секундный интервал говорит "ACW" , Я предположил, что это просто ошибка, и мои результаты имеют последнее значение.

Схема запроса:

  • Сначала мы создаем mytable2, который берет данные из вашего примера " mytable "и преобразует время начала и окончания в Oracle даты.

  • Затем мы создаем q1, в котором есть одна строка, в которой столбец m1 находится за час до данных и Столбец m2 находится в часе после данных.

  • Затем мы создаем q2, у которого есть начальная точка всех 15-минутных интервалов, в которых мы заинтересованы. Используя connect by ... rownum - это общий шаблон всякий раз, когда вам нужны последовательные числа.

Наконец, фактический запрос представляет собой четырехстороннее объединение, в котором каждая ветвь соединяет q2 и mytable2.

  • Ветка # 1 - это когда интервал полностью находится внутри 15-минутного сегмента.
  • Ветка # 2 - это когда 15-минутный сегмент полностью находится в интервале.
  • Ветка # 3 - это где интервал начинается до и заканчивается в течение e 15-минутный сегмент.
  • В филиале № 4 интервал начинается и заканчивается после 15-минутного сегмента.
with mytable2 as (
    select ID, date_ as "Date",
    cast(to_timestamp(date_ || ' ' ||  START_TIME, 'dd/mon/yy HH:MI:SS.FF AM') as date) "Start Time",
    cast(to_timestamp(date_ || ' ' || END_TIME, 'dd/mon/yy HH:MI:SS.FF AM')as date) "End Time",
    secs,
    state,
    substate
    from mytable
), q1 as (
select trunc(min("Start Time"), 'hh') as m1, trunc(max("End Time")+1/24, 'hh') as m2
from mytable2
),
q2 as (
select m1 + (rownum-1)/24/4 as b1, m1 + rownum/24/4 as b2
from q1, dual
connect by rownum <= (m2-m1)*24*4
)
select "ID", "Date", "15 Int", "Secs", "State", "SubState" from (
select ID, "Date", "Start Time", to_char(b1, 'HH:MI AM') as "15 Int",  secs as "Secs", state as "State", substate as "SubState" from mytable2 m
join
 q2 on "Start Time" >= b1 and "End Time" <= b2
 union
 select ID, "Date",b1 as "Start Time",  to_char(b1, 'HH:MI AM') as "15 Int", 900 , state, substate from mytable2 m
join
 q2 on "Start Time" < b1 and "End Time" > b2
 union
 select ID, "Date",b1 as "Start Time",  to_char(b1, 'HH:MI AM') as "15 Int",  round(("End Time" - b1)*24*3600,0) , state, substate from mytable2 m
join
 q2 on "Start Time" < b1 and ("End Time" between b1 and  b2)
 union
 select ID, "Date","Start Time", to_char(b1, 'HH:MI AM') as "15 Int",  round((b2-"Start Time")*24*3600,0), state, substate from mytable2 m
join
 q2 on ("Start Time" between b1 and b2 )and "End Time" > b2
) q 
order by q.ID, q."Start Time"
+-------+-----------+----------+------+----------+----------+
|  ID   |   Date    |  15 Int  | Secs |  State   | SubState |
+-------+-----------+----------+------+----------+----------+
| 10001 | 18/MAR/20 | 09:00 AM |  170 | Ready    | Ready    |
| 10001 | 18/MAR/20 | 09:00 AM |  329 | NotReady | Busy     |
| 10001 | 18/MAR/20 | 09:15 AM |  900 | NotReady | Busy     |
| 10001 | 18/MAR/20 | 09:30 AM |  881 | NotReady | Busy     |
| 10001 | 18/MAR/20 | 09:30 AM |   19 | NotReady | ACW      |
| 10001 | 18/MAR/20 | 09:45 AM |  391 | NotReady | ACW      |
| 10001 | 18/MAR/20 | 09:45 AM |  174 | NotReady | Busy     |
| 10001 | 18/MAR/20 | 09:45 AM |   35 | NotReady | ACW      |
+-------+-----------+----------+------+----------+----------+

Я использовал следующую таблицу для тестирования :

create table mytable as
select '10001' ID,'18/MAR/20' DATE_, '09:06:41.000000000 AM' START_TIME,'09:09:31.000000000 AM' END_TIME,170  secs, 'Ready' State, 'Ready' substate from dual
union all select '10001' ID,'18/MAR/20' DATE_, '09:09:31.000000000 AM' START_TIME,'09:44:41.000000000 AM' END_TIME,2110 secs, 'NotReady' State, 'Busy' substate from dual
union all select '10001' ID,'18/MAR/20' DATE_, '09:44:41.000000000 AM' START_TIME,'09:51:31.000000000 AM' END_TIME,410  secs, 'NotReady' State, 'ACW' substate from dual
union all select '10001' ID,'18/MAR/20' DATE_, '09:51:31.000000000 AM' START_TIME,'09:54:25.000000000 AM' END_TIME,174  secs, 'NotReady' State, 'Busy' substate from dual
union all select '10001' ID,'18/MAR/20' DATE_, '09:54:25.000000000 AM' START_TIME,'09:55:00.000000000 AM' END_TIME,35   secs, 'NotReady' State, 'ACW' substate from dual
+-------+-----------+-----------------------+-----------------------+------+----------+----------+
|  ID   |   DATE_   |      START_TIME       |       END_TIME        | SECS |  STATE   | SUBSTATE |
+-------+-----------+-----------------------+-----------------------+------+----------+----------+
| 10001 | 18/MAR/20 | 09:06:41.000000000 AM | 09:09:31.000000000 AM |  170 | Ready    | Ready    |
| 10001 | 18/MAR/20 | 09:09:31.000000000 AM | 09:44:41.000000000 AM | 2110 | NotReady | Busy     |
| 10001 | 18/MAR/20 | 09:44:41.000000000 AM | 09:51:31.000000000 AM |  410 | NotReady | ACW      |
| 10001 | 18/MAR/20 | 09:51:31.000000000 AM | 09:54:25.000000000 AM |  174 | NotReady | Busy     |
| 10001 | 18/MAR/20 | 09:54:25.000000000 AM | 09:55:00.000000000 AM |   35 | NotReady | ACW      |
+-------+-----------+-----------------------+-----------------------+------+----------+----------+
...