查找不规则日期的小时百分比

时间:2011-05-16 21:03:05

标签: sql oracle datetime intervals

我有一个如下表格,每个条目在给定时间内改变状态。 状态可以重复,因为其他列具有子状态信息。

如何按小时计算每个状态的百分比时间?

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 个答案:

答案 0 :(得分:4)

很棒的问题,这是一个有趣的挑战!

您需要的是一个辅助表来存储每个时间段(在本例中为小时),然后加入到状态更新重叠的位置。 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语句以获得所需的百分比。

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.