2011-05-16 33 views
4

我有一張如下所示的表格,每個條目都在給定時間內改變STATUS。 該狀態可以重複,因爲其他列具有子狀態信息。從非正常日期中每小時查找百分比

我怎樣才能獲得每個狀態的時間百分比?

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. 

回答

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. 
+1

好的解決方案。您不一定需要額外的表格 - 您可以在查詢中即時生成它,例如使用'從雙連接水平<= 24'技巧的選擇ROWNUM。 – 2011-05-17 01:27:09

+0

優雅。我希望你在你工作的地方工資很高。 – 2011-05-17 19:31:35

+0

@Jeffrey Kemp我必須對你的建議做一些閱讀。我沒有使用CONNECT BY LEVEL。聽起來不錯! – 2011-05-17 19:32:17