2011-05-16 2 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

좋은 해결책. 반드시 여분의 테이블이 필요하지는 않습니다. 쿼리에서 온 - 더 - 플라이 (on-the-fly)로 생성 할 수 있습니다 (예 : 'DUAL FROM DOME CONNECT BY LEVEL <= 24 '트릭을 사용합니다. –

+0

우아한. 나는 네가 일하는 곳에서 돈을 잘 받길 바랍니다. –

+0

@ Jeffrey Kemp 귀하의 제안에 대해 약간의 독서를해야 할 것입니다. CONNECT BY LEVEL을 사용하지 않았습니다. 흥미로운 소리! –