Поиск процента времени за час от нерегулярных дат - PullRequest
4 голосов
/ 17 мая 2011

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

Как я могу получить процентное время для каждого статуса, скажем, часом?

NAME STATUS_CHANGE_TIME  STATUS
foo  15-MAY-11 18:52     A
foo  15-MAY-11 18:38     A
foo  15-MAY-11 18:33     B
foo  15-MAY-11 16:53     A
foo  15-MAY-11 16:47     B
foo  15-MAY-11 13:37     A
foo  15-MAY-11 13:33     C
foo  15-MAY-11 10:23     C
foo  15-MAY-11 10:17     A
foo  ...

Желаемый возврат:

HH24  STATUS  PERCENT    
10  ...
11    C       100        (No entries; last change was to C)
12    C       100        ""                       ""
13    C        62
13    A        38        (From C to A at :37 with 23 mins left; 23/60 ~ 38%)
14    A       100
15    A       100
16    A        90        (= A for first 47 minutes, then for another 7)
16    B        10        (16:53 - 16:47 = 6 minutes or 10% of an hour)
17    A       100
18 ... etc.

1 Ответ

4 голосов
/ 17 мая 2011

Отличный вопрос, это был интересный вызов!

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

Конечно, это гораздо проще объяснить на примере. Вот таблица HOURS, необходимая:

SQL> CREATE TABLE hours (HOUR NUMBER(2), start_m date, end_m date);

Table created.

SQL> BEGIN
  2      FOR i IN 0..23 LOOP
  3          INSERT INTO hours VALUES(i, to_date(lpad(i, 2, '0')||':00:00', 'HH24:MI:SS')
  4                                    , to_date(lpad(i, 2, '0')||':59:59', 'HH24:MI:SS'));
  5      END loop;
  6      COMMIT;
  7  END;
  8  /

PL/SQL procedure successfully completed.

Ниже приводится просто набор ваших тестовых данных из вашего вопроса.

SQL> CREATE TABLE status_updates (NAME VARCHAR2(3), status_change_time DATE, status CHAR(1));

Table created.

SQL> INSERT INTO status_updates VALUES ('foo',TO_DATE('15-MAY-11 18:52', 'DD-MON-RR HH24:MI:SS'), 'A');

1 row created.

SQL> INSERT INTO status_updates VALUES ('foo',TO_DATE('15-MAY-11 18:38', 'DD-MON-RR HH24:MI:SS'), 'A');

1 row created.

SQL> INSERT INTO status_updates VALUES ('foo',TO_DATE('15-MAY-11 18:33', 'DD-MON-RR HH24:MI:SS'), 'B');

1 row created.

SQL> INSERT INTO status_updates VALUES ('foo',TO_DATE('15-MAY-11 16:53', 'DD-MON-RR HH24:MI:SS'), 'A');

1 row created.

SQL> INSERT INTO status_updates VALUES ('foo',TO_DATE('15-MAY-11 16:47', 'DD-MON-RR HH24:MI:SS'), 'B');

1 row created.

SQL> INSERT INTO status_updates VALUES ('foo',TO_DATE('15-MAY-11 13:37', 'DD-MON-RR HH24:MI:SS'), 'A');

1 row created.

SQL> INSERT INTO status_updates VALUES ('foo',TO_DATE('15-MAY-11 13:33', 'DD-MON-RR HH24:MI:SS'), 'C');

1 row created.

SQL> INSERT INTO status_updates VALUES ('foo',TO_DATE('15-MAY-11 10:23', 'DD-MON-RR HH24:MI:SS'), 'C');

1 row created.

SQL> INSERT INTO status_updates VALUES ('foo',TO_DATE('15-MAY-11 10:17', 'DD-MON-RR HH24:MI:SS'), 'A');

1 row created.

SQL> commit;

Commit complete.

Теперь вот оператор выбора, чтобы получить требуемые проценты.

SELECT t.NAME, t.HOUR, t.status, sum(round((status_end_h-start_status_h)*24*100)) per_cent
FROM   (
    SELECT A.NAME
    ,      A.status
    ,      A.status_change_time
    ,      A.next_change_time
    ,      b.HOUR
    ,      greatest(status_change_time, trunc(status_change_time)+(b.start_m-trunc(b.start_m))) start_status_h
    ,      least(next_change_time, trunc(next_change_time)+(b.end_m-trunc(b.end_m))) status_end_h
    FROM   (
        SELECT NAME
        ,      status
        ,      status_change_time
        ,      lead(status_change_time) OVER (ORDER BY NAME, status_change_time) next_change_time
        FROM   status_updates
    ) A, hours b
    WHERE  TO_CHAR(b.start_m, 'HH24:MI:SS') BETWEEN TO_CHAR(A.status_change_time, 'HH24:MI:SS') AND TO_CHAR(A.next_change_time, 'HH24:MI:SS')
    OR     TO_CHAR(b.end_m, 'HH24:MI:SS') BETWEEN TO_CHAR(A.status_change_time, 'HH24:MI:SS') AND TO_CHAR(A.next_change_time, 'HH24:MI:SS')
    OR    (TO_CHAR(A.status_change_time, 'HH24:MI:SS') BETWEEN TO_CHAR(b.start_m, 'HH24:MI:SS') AND TO_CHAR(b.end_m, 'HH24:MI:SS')
    AND    TO_CHAR(A.next_change_time, 'HH24:MI:SS') BETWEEN TO_CHAR(b.start_m, 'HH24:MI:SS') AND TO_CHAR(b.end_m, 'HH24:MI:SS'))
) t
GROUP BY t.NAME, t.HOUR, t.status
ORDER BY t.HOUR;

NAM       HOUR S   PER_CENT                                                     
--- ---------- - ----------                                                     
foo         10 A         10                                                     
foo         10 C         62                                                     
foo         11 C        100                                                     
foo         12 C        100                                                     
foo         13 A         38                                                     
foo         13 C         62                                                     
foo         14 A        100                                                     
foo         15 A        100                                                     
foo         16 A         90                                                     
foo         16 B         10                                                     
foo         17 A        100                                                     

NAM       HOUR S   PER_CENT                                                     
--- ---------- - ----------                                                     
foo         18 A         78                                                     
foo         18 B          8                                                     

13 rows selected.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...