안녕하세요. 주차 관련해서 데이터를 생성하려고 하는데 도움 부탁드립니다.
아래와 같은 오라클 데이터가 있다고 가정을 하고
데이터 타입은 YEAR, MONTH, COST 모두 Number형입니다.
YEAR | MONTH | COST |
2022 | 12 | 199 |
2023 | 1 | 195 |
2023 | 3 | 100 |
위 데이터를 이용해서 아래와 같은 데이터를 만들고 싶은데 방법이 있을까요?
예를 들면 2023년 1월에 195이라는 데이터가 있다면 1월에 해당하는 주차를 만들고 각 주차별로 해당월의 데이터를 넣어주고 싶어요.
주차의 시작은 일요일을 기준으로 합니다.
YEAR | MONTH | WEEK | COST |
2022 | 12 | 49 | 199 |
2022 | 12 | 50 | 199 |
2022 | 12 | 51 | 199 |
2022 | 12 | 52 | 199 |
2023 | 1 | 1 | 195 |
2023 | 1 | 2 | 195 |
2023 | 1 | 3 | 195 |
2023 | 1 | 4 | 195 |
2023 | 1 | 5 | 195 |
2023 | 3 | 10 | 100 |
2023 | 3 | 11 | 100 |
2023 | 3 | 12 | 100 |
2023 | 3 | 13 | 100 |
감사합니다.
WITH t AS ( SELECT 2022 year, 12 month, 199 cost FROM dual UNION ALL SELECT 2023, 1, 195 FROM dual UNION ALL SELECT 2023, 3, 100 FROM dual ) SELECT year, month, cost , w , (s - y) / 7 + w week , TO_CHAR(s + w*7 - 6, 'yyyymmdd') sdt , TO_CHAR(s + w*7 , 'yyyymmdd') edt FROM (SELECT year, month, cost , NEXT_DAY(TO_DATE(year||'01' , 'yyyymm') - 1, 1) y -- 연 첫번째 일요일(연 1주차 시작일) , NEXT_DAY(TO_DATE(year||month, 'yyyymm') - 1, 1) s -- 월 첫번째 일요일(월 1주차 시작일) , LAST_DAY(TO_DATE(year||month, 'yyyymm')) e -- 월 마지막 일자 FROM t ) , (SELECT LEVEL w FROM dual CONNECT BY LEVEL <= 5) WHERE w <= (e - s) / 7 + 1 ORDER BY year, month, w ;
추가 질문인데요. 2월은 원본 테이블에도 데이터가 없습니다.
이럴경우 결과에는 2월을 NULL 값으로 데이터를 만들 수가 있을까요?
아래와 같이...
2022 12 199 1 49 20221204 20221210
2022 12 199 2 50 20221211 20221217
2022 12 199 3 51 20221218 20221224
2022 12 199 4 52 20221225 20221231
2023 1 195 1 1 20230101 20230107
2023 1 195 2 2 20230108 20230114
2023 1 195 3 3 20230115 20230121
2023 1 195 4 4 20230122 20230128
2023 1 195 5 5 20230129 20230204
2023 2 NULL 1 6 20230205 20230211
2023 2 NULL 2 7 20230212 20230218
2023 2 NULL 3 8 20230219 20230225
2023 2 NULL 4 9 20230226 20230304
2023 3 100 1 10 20230305 20230311
2023 3 100 2 11 20230312 20230318
2023 3 100 3 12 20230319 20230325
2023 3 100 4 13 20230326 20230401
감사합니다.
WITH t AS
(
SELECT 2022 year, 12 month, 199 cost FROM dual
UNION ALL SELECT 2023, 1, 195 FROM dual
UNION ALL SELECT 2023, 3, 100 FROM dual
)
SELECT year, month, cost
, w
, (s - y) / 7 + w week
, TO_CHAR(s + w*7 - 7, 'yyyymmdd') sdt
, TO_CHAR(s + w*7 - 1, 'yyyymmdd') edt
FROM (SELECT year, month, cost
, NEXT_DAY(TO_DATE(year||'01' , 'yyyymm') - 1, 1) y -- 연 첫번째 일요일(연 1주차 시작일)
, NEXT_DAY(TO_DATE(year||month, 'yyyymm') - 1, 1) s -- 월 첫번째 일요일(월 1주차 시작일)
, LAST_DAY(TO_DATE(year||month, 'yyyymm')) e -- 월 마지막 일자
FROM t
)
, (SELECT LEVEL w FROM dual CONNECT BY LEVEL <= 5)
WHERE w <= (e - s) / 7 + 1
ORDER BY year, month, w
;
sdt와 edt 날짜를 하루씩 더 빼줬어요.
WITH t AS ( SELECT 2022 year, 12 month, 199 cost FROM dual UNION ALL SELECT 2023, 1, 195 FROM dual UNION ALL SELECT 2023, 3, 100 FROM dual ), t2 AS ( SELECT TO_CHAR(DT, 'yyyymmdd') SDT , TO_CHAR(DT, 'WW') WEEK FROM ( SELECT TO_DATE('20221211','YYYYMMDD') + LEVEL - 1 DT , NEXT_DAY(SYSDATE-7, 1) THISDATE FROM DUAL CONNECT BY LEVEL <= TO_DATE('20230326','YYYYMMDD') - TO_DATE('20221211' ,'YYYYMMDD') + 1 ) WHERE TO_CHAR(DT,'D') = '1' ), t3 AS ( SELECT year, month, cost , w , (s - y) / 7 + w week , TO_CHAR(s + w*7 - 7, 'yyyymmdd') sdt , TO_CHAR(s + w*7 - 1, 'yyyymmdd') edt FROM (SELECT year, month, cost , NEXT_DAY(TO_DATE(year||'01' , 'yyyymm') - 1, 1) y -- 연 첫번째 일요일(연 1주차 시작일) , NEXT_DAY(TO_DATE(year||month, 'yyyymm') - 1, 1) s -- 월 첫번째 일요일(월 1주차 시작일) , LAST_DAY(TO_DATE(year||month, 'yyyymm')) e -- 월 마지막 일자 FROM t ) , (SELECT LEVEL w FROM dual CONNECT BY LEVEL <= 5) WHERE w <= (e - s) / 7 + 1 ORDER BY year, month, w ) SELECT t2.WEEK , t3.cost FROM t2, t3 WHERE t2.WEEK = t3.WEEK(+) ORDER BY t2.week ;
이런식으로 날짜를 기준으로 임시 테이블을 만들고 outer join으로 쿼리를 만들었어요.
이렇게 원하는 데이터는 나오는데...
원시 테이블에 데이터가 많아지면 검색 속도가 느려진다는 문제가 있네요. ㅠㅠ
WITH t AS ( SELECT 2022 year, 12 month, 199 cost FROM dual UNION ALL SELECT 2023, 1, 195 FROM dual UNION ALL SELECT 2023, 3, 100 FROM dual ) SELECT a.year , a.month , a.week , a.sdt , a.edt , b.cost FROM (SELECT TO_CHAR(sdt, 'yyyy') year , TO_CHAR(sdt, 'mm') month , TO_CHAR(sdt, 'ww') week , TO_CHAR(sdt, 'yyyymmdd') sdt , TO_CHAR(edt, 'yyyymmdd') edt FROM (SELECT sdt + LEVEL*7 - 7 sdt , sdt + LEVEL*7 - 1 edt FROM (SELECT TRUNC(TO_DATE('20221211', 'yyyymmdd'), 'd') sdt , TRUNC(TO_DATE('20230326', 'yyyymmdd'), 'd') edt FROM dual ) CONNECT BY LEVEL <= (edt - sdt) / 7 + 1 ) ) a LEFT OUTER JOIN t b ON a.year = b.year AND a.month = b.month ORDER BY year, month, week ;