큰 문제는,이 흥미로운 도전이었다!
필요한 경우 각 시분할 (이 경우 시간)을 저장하고 상태 업데이트가 겹치는 곳에 참여하는 보조 테이블이 필요합니다. 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.
좋은 해결책. 반드시 여분의 테이블이 필요하지는 않습니다. 쿼리에서 온 - 더 - 플라이 (on-the-fly)로 생성 할 수 있습니다 (예 : 'DUAL FROM DOME CONNECT BY LEVEL <= 24 '트릭을 사용합니다. –
우아한. 나는 네가 일하는 곳에서 돈을 잘 받길 바랍니다. –
@ Jeffrey Kemp 귀하의 제안에 대해 약간의 독서를해야 할 것입니다. CONNECT BY LEVEL을 사용하지 않았습니다. 흥미로운 소리! –