안녕하세요.
몇일 쿼리로 고생하다가 질문 드립니다.
아래 데이터를 기초로
No | 상품명 | 시작일 | 종료일 | 금액 |
1 | A | 20150916 | 20150922 | 4000 |
2 | A | 20150918 | 20150918 | 8000 |
3 | A | 20150923 | 99991231 | 7000 |
4 | A | 20151014 | 20151014 | 3000 |
다음과 같은 데이터를 만들어야 합니다.
No | 상품명 | 시작일 | 종료일 | 금액 |
1 | A | 20150916 | 20150917 | 4000 |
2 | A | 20150918 | 20150918 | 8000 |
3 | A | 20150919 | 20150922 | 4000 |
4 | A | 20150923 | 20151013 | 7000 |
5 | A | 20151014 | 20151014 | 3000 |
6 | A | 20151015 | 99991231 | 7000 |
예를 들어 1번 행과 같이 9/16 ~ 9/22 데이터가 있고
2번 9/18 ~ 9/18 데이터가 있다면
2번이 1번 시작일 ~ 종료일에 포함되는 데이터 이기 때문에
9/16 ~ 9/17, 9/18 ~ 9/18, 9/19 ~ 9/22 이렇게 3개행으로 쪼개져야 합니다.
설명이 좀 부족한것 같다는 생각이 듭니다만.... 표현이 잘 안되네요 ㅠ.ㅠ
temp테이블을 이용하거나, 프로시저로 작성해되 되는데
어떻게 해야할지 감이 안잡히네요...
비슷한 경우를 해결해 보신 분이 계시면 도움 부탁드립니다.
비슷한 유형의 문제를 풀어본적이 있습니다.
http://www.gurubee.net/lecture/2848
그런데 요구조건이 조금 다르네요.
같은 방식으로 풀기는 어려울것 같아,
날짜별로 분리 한뒤 다시 그룹바이 하는 방식으로 풀어봤습니다.
99991231 에 대한 부담으로 20201231 로 변경하여 처리했습니다.
WITH t AS ( SELECT 1 no, 'A' cd, '20150916' sdt, '20150922' edt, 4000 amt FROM dual UNION ALL SELECT 2, 'A', '20150918', '20150918', 8000 FROM dual UNION ALL SELECT 3, 'A', '20150923', '99991231', 7000 FROM dual UNION ALL SELECT 4, 'A', '20151014', '20151014', 3000 FROM dual UNION ALL SELECT 5, 'A', '20151114', '20151116', 5000 FROM dual ) SELECT cd , TO_CHAR(MIN(dt), 'yyyymmdd') sdt , REPLACE(TO_CHAR(MAX(dt), 'yyyymmdd'), '20201231', '99991231') edt , amt FROM (SELECT cd, dt, no, amt , dt - ROW_NUMBER() OVER(PARTITION BY cd, no, amt ORDER BY dt) grp FROM (SELECT cd , sdt + lv - 1 dt , MAX(no) no , MAX(amt) KEEP(DENSE_RANK LAST ORDER BY no) amt FROM (SELECT no, cd, amt , TO_DATE(sdt, 'yyyymmdd') sdt , TO_DATE(DECODE(edt, '99991231', '20201231', edt), 'yyyymmdd') edt FROM t ) , (SELECT LEVEL lv FROM dual CONNECT BY LEVEL <= 9999) WHERE lv <= edt - sdt + 1 GROUP BY cd, sdt + lv - 1 ) ) GROUP BY cd, grp, no, amt ORDER BY cd, sdt ;
http://www.gurubee.net/lecture/2848
링크의 방법(선분이력을 점이력으로 변경하여 처리하는 방법)을 적용해 봤습니다.
이 방법을 적용하기 위해 원본데이터를 적용가능한 형태(tmp)로 우선 가공했습니다.
WITH t AS ( SELECT 1 no, 'A' cd, '20150916' sdt, '20150922' edt, 4000 amt FROM dual UNION ALL SELECT 2, 'A', '20150918', '20150918', 8000 FROM dual UNION ALL SELECT 3, 'A', '20150923', '99991231', 7000 FROM dual UNION ALL SELECT 4, 'A', '20151014', '20151014', 3000 FROM dual UNION ALL SELECT 5, 'A', '20151114', '20151119', 5000 FROM dual UNION ALL SELECT 6, 'A', '20151115', '20151115', 6000 FROM dual ) , tmp AS ( SELECT a.no, a.cd , TO_DATE(a.sdt, 'yyyymmdd') sdt , TO_DATE(a.edt, 'yyyymmdd') edt , a.amt - NVL(MAX(b.amt) KEEP(DENSE_RANK LAST ORDER BY b.no), 0) amt FROM t a , t b WHERE a.cd = b.cd(+) AND a.sdt > b.sdt(+) AND a.sdt < b.edt(+) GROUP BY a.no, a.cd, a.sdt, a.edt, a.amt ) SELECT cd , TO_CHAR(sdt, 'yyyymmdd') sdt , NVL(TO_CHAR(edt, 'yyyymmdd'), '99991231') edt , amt FROM (SELECT cd, sdt , LEAD(sdt - 1) OVER(ORDER BY sdt) edt , SUM(SUM(amt)) OVER(ORDER BY sdt) amt FROM (SELECT cd , DECODE(lv, 1, sdt, edt + 1) sdt , DECODE(lv, 1, amt, -amt) amt FROM tmp , (SELECT LEVEL lv FROM dual CONNECT BY LEVEL <= 2) WHERE lv = 1 OR edt != '99991231' ) GROUP BY cd, sdt ) WHERE amt != 0 ;