시간선분 분할 및 중첩구간 합 관련 쿼리 문의 0 10 2,398

by 박균필 [SQL Query] 선분이력 시간선분 [2011.02.24 16:46:59]


Q.PNG (33,907Bytes)


아래와 같은 기준 테이블이 있습니다.

with t as ( 

SELECT 'A' GK, '20100301' sdt, '20100701' edt, 10 cnt FROM dual UNION ALL

SELECT 'A' GK, '20100401' sdt, '20100501' edt, 20 FROM dual UNION ALL

SELECT 'A' GK, '20100501' sdt, '20100701' edt, 30 FROM dual UNION ALL

SELECT 'B' GK, '20100601' sdt, '20100901' edt, 40 FROM dual UNION ALL

SELECT 'B' GK, '20101001' sdt, '20101101' edt, 50 FROM dual UNION ALL

SELECT 'B' GK, '20100311' sdt, '20100601' edt, 60 FROM dual UNION ALL

SELECT 'B' GK, '20100401' sdt, '20100701' edt, 20 FROM dual
)

이를 중첩된 구간을 분할하여 중첩되는 구간의 cnt의 합을 구하려고 합니다.
(첨부 참조)

고민 끝에 구간을 분할하는것은 가능할것 같은데
중첩된 구간의 sum을 도저히 못구하겠네요...
몇일째 고민중입니다. 도움을 좀 주세요..^^

결과는 아래와 같습니다.(그림의 결과는 잘못된 결과입니다.)
A    10    20100301    20100401
A    30    20100401    20100501
A    40    20100501    20100701
B    60    20100311    20100401
B    80    20100401    20100601
B    60    20100601    20100701
B    40    20100701    20100901
B    50    20101001    20101101

by 문두 [2011.02.24 18:36:30]
속도상관없다면 이렇게라도 ㅡㅡㅋ;;

with t as (
SELECT 'A' GK, '20100301' sdt, '20100701' edt, 10 cnt FROM dual UNION ALL
SELECT 'A' GK, '20100401' sdt, '20100501' edt, 20 FROM dual UNION ALL
SELECT 'A' GK, '20100501' sdt, '20100701' edt, 30 FROM dual UNION ALL
SELECT 'B' GK, '20100601' sdt, '20100901' edt, 40 FROM dual UNION ALL
SELECT 'B' GK, '20101001' sdt, '20101101' edt, 50 FROM dual UNION ALL
SELECT 'B' GK, '20100311' sdt, '20100601' edt, 60 FROM dual UNION ALL
SELECT 'B' GK, '20100401' sdt, '20100701' edt, 60 FROM dual
)
SELECT A.GK, (SELECT SUM (X1.CNT)
FROM T X1
WHERE X1.GK = A.GK AND A.EDT > X1.SDT AND A.EDT <= X1.EDT) AS CNT, A.SDT, A.EDT
FROM (SELECT FLAG, GK, DT AS SDT, LEAD (DT) OVER (ORDER BY GK, DT) AS EDT
FROM (SELECT DISTINCT GK, CASE
WHEN SUM (LVL) OVER (ORDER BY GK, DT) > 0
THEN 1
END AS FLAG, DT
FROM (SELECT LVL, GK, CASE
WHEN LVL = 1
THEN SDT
ELSE EDT
END AS DT
FROM T
, (SELECT CASE
WHEN LEVEL = 1
THEN 1
ELSE -1
END AS LVL
FROM DUAL
CONNECT BY LEVEL <= 2)))) A
WHERE A.FLAG = 1

맞는지는모르겟네요;

by 박균필 [2011.02.24 19:31:21]
집계대상이 되는 T가 실제 쿼리에서는 상당히 복잡한 형태입니다.
속도때문에 이와 같이 하기에는 무리가 있을것 같네요..
다른 방법이 없을까요?

by finecomp [2011.02.25 01:45:58]

SELECT t1.gk, t1.dt1, t1.dt2, SUM(t2.cnt) AS sum
FROM(
SELECT gk, DECODE(cp.no, 1, sdt, edt) AS dt1
, LEAD(DECODE(cp.no, 1, sdt, edt)) OVER(PARTITION BY gk ORDER BY DECODE(cp.no, 1, sdt, edt)) AS dt2
FROM t, (SELECT LEVEL AS no FROM dual CONNECT BY LEVEL <= 2) cp
GROUP BY gk, DECODE(cp.no, 1, sdt, edt)
) t1, t t2
WHERE t2.gk = t1.gk
AND t2.sdt <= t1.edt AND t2.edt => t1.sdt
GROUP BY t1.gk, t1.dt1, t1.dt2
;

원하는 결과가 아니라면 결과 sum이 저렇게 나오게 되는 규칙 설명이라도...;

by camela [2011.02.25 09:25:45]
단순하게 sum 만 하면 되는건가요?
구간을 분해하면 분해된 구간들이 모두 같은 cnt 값을 가질 수 밖에 없는데 맞는건가요?

by 쏘쿨 [2011.02.25 09:50:37]
제가 냈던 퀴즈 문제와 아주 유사한 문제군요.

by 마농 [2011.02.25 10:06:40]
WITH t AS
(
SELECT 'A' gk, '20100301' sdt, '20100701' edt, 10 cnt FROM dual
UNION ALL SELECT 'A', '20100401', '20100501', 20 FROM dual
UNION ALL SELECT 'A', '20100501', '20100701', 30 FROM dual
UNION ALL SELECT 'B', '20100601', '20100901', 40 FROM dual
UNION ALL SELECT 'B', '20101001', '20101101', 50 FROM dual
UNION ALL SELECT 'B', '20100311', '20100601', 60 FROM dual
UNION ALL SELECT 'B', '20100401', '20100701', 20 FROM dual
)
SELECT a.gk, a.sdt, a.edt
, SUM(b.cnt)
FROM
(
SELECT gk
, DECODE(gb,1,sdt,edt) sdt
, LEAD(DECODE(gb,1,sdt,edt)) OVER(PARTITION BY gk ORDER BY DECODE(gb,1,sdt,edt)) edt
, SUM(SUM(gb)) OVER(PARTITION BY gk ORDER BY DECODE(gb,1,sdt,edt)) flag
, SUM(cnt) cnt
FROM t
, (SELECT 3 - LEVEL * 2 gb FROM dual CONNECT BY LEVEL <= 2)
GROUP BY gk, DECODE(gb,1,sdt,edt)
) a
, t b
WHERE a.flag > 0
AND a.gk = b.gk
AND a.edt > b.sdt
AND a.sdt < b.edt
GROUP BY a.gk, a.sdt, a.edt
ORDER BY a.gk, a.sdt
;

by 박균필 [2011.02.25 13:12:02]
답변 주신분들 감사합니다.
결과만을 봤을때 답변주신분들의 답이 대부분 맏는것 같습니다.
속도부분이 문제일것 같습니다.
T의 엑세스를 한번만 해서 구할수 있는 방법은 없을까요...
T의 Data가 상당히 많습니다.
T의 Data 또한 상당한 가공을 거쳐 얻어집니다.
다시한번 답변주신분들께 감사드립니다..^^
즐거운 주말 되세요..

by 마농 [2011.02.25 13:15:50]
상당한 가공을 거친 데이터를 다시 가공하고자 하신다면
저라면 원본데이터로부터 결과를 바로 뽑을 수 있는 방법은 없는지 고민해 보겠습니다.

by 박균필 [2011.02.25 15:54:45]
원본데이타로 할수있는 방법도 조금 힘듭니다.
원본데이타도 여러 데이타의 min, max로 해서 구간이 결정되구요..
데이타 또한 조건에 따라 connect by를 통해 여러 row로 가공을 해야 하구요..
좀.. 어렵네요..
원본의 건수가 약 3만건이 넘습니다..

by 마농 [2011.02.25 17:27:12]
물론 쉽진 않겠지요.
하지만 A 라는 원본을 가지고 B 라는 결과를 얻었을때 그과정이 최선의 과정이었다고 해도
B 와는 다른 결과인 C라는 결과를 얻기 위해 A>B>C 의 과정을 거치는 것이 최선이 아닐수도 있습니다.
A>C 로 바로 가거나 A>D>C 의 전혀 다른 방법으로 접근을 한다거나 할 수 있겠죠.
SQL은 절차적 언어가 아닙니다. 집합적 언어구요.
절차가 중요한것이 아니라 집합적 사고가 중요하죠.
중간집합을 한정해 놓으면 오히려 생각의 범위도 그만큼 줄어듭니다.
어떠한 중간집합이 올지 정해지지 않은 상태에서 접근해야 여러가지 아이디어를 도출할 수 있습니다.
꼭 B 가 나쁘다는것은 아닙니다.
원본자료도 모르고 B도출 쿼리도 모르니 섯부르게 판단할 문제는 아니죠.
댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입