기준일자를 입력하였을때, 해당 기준일자(당월)를 기준으로 한달전 날짜들을 도출하여 일자를 표기한다.
SELECT ADD_MONTHS(TO_DATE(:base_yrmn || LPAD(LEVEL, 2, '0'), 'YYYYMMDD'), -1) AS BASE_DATE
FROM DUAL
CONNECT BY ADD_MONTHs(TO_DATE(:base_yrmn||'01', 'YYYYMMDD'), -1) + LEVEL -1 <= LAST_DAY(ADD_MONTHS(TO_DATE(:base_yrmn||'01', 'YYYYMMDD'), -1))
해당 쿼리의 문제점은
본 쿼리는 기준일자가 31일까지 존재하는 달에 대해서만 적용이 가능하는 등의 몇가지 개선사항이 존재한다.
쿼리를 수정해가면서 날짜를 도출하는 방법을 다르게 하여 진행한다.
본 쿼리는 기준일자를 입력한 후 전월/당월/익월을 모두 도출하므로, 속도면에서 불리할 수 있다.
관련한 모든 일자를 불러온후, 주차수(WKLY_SRNO)를 기준으로 출력할 일자를 도출한다.
SELECT TO_DATE(:base_yrmn -1 || LPAD(LEVEL, 2, '0'), 'YYYYMMDD') AS BASE_DATE
FROM DUAL
CONNECT BY ADD_MONTHS(TO_DATE(:base_yrmn || '01', 'YYYYMMDD'), -1) + LEVEL - 1 <= LAST_DAY(ADD_MONTHS(TO_DATE(:base_yrmn||'01', 'YYYYMMDD'), -1))
UNION ALL
SELECT TO_DATE(:base_yrmn | LPAD(LEVEL, 2, '0'), 'YYYYMMDD') AS BASE_DATE
FROM DUAL
CONNECT BY TO_DATE(:base_yrmn || '01', 'YYYYMMDD') + LEVEL - 1 <= LAST_DAY(TO_DATE(:base_yrmn || '01', 'YYYYMMDD'))
UNION ALL
SELECT TO_DATE(:base_yrmn +1 || LPAD(LEVEL, 2, '0'), 'YYYYMMDD') AS BASE_DATE
FROM DUAL
CONNECT BY ADD_MONTHS(TO_DATE(:base_yrmn || '01', 'YYYYMMDD'), -1) + LEVEL - 1 <= LAST_DAY(ADD_MONTHS(TO_DATE(:base_yrmn||'01', 'YYYYMMDD'), +1))
본 쿼리는 도출할 표기일자에 대한 범위를 먼저 나타내주는 쿼리이다.
년도가 넘어갈때의 예외처리를 위한 임시방편이고, 쿼리가 길어질뿐더러 주차수를 계산할때 더 복잡해지는 쿼리이므로 다른 효율적인 방안을 고민해보도록 한다.
올해 -> 전년도일 경우
SELECT TO_DATE(DECODE(SUBSTR(:base_yrmn -1, 5, 2), 00, :base_yrmn - 89, :base_yrmn -1) || LPAD(LEVEL, 2, '0'), 'YYYYMMDD') AS BASE_DATE
FROM DUAL
CONNECT BY ADD_MONTHS(TO_DATE(:base_yrmn || '01', 'YYYYMMDD'), -1) + LEVEL -1 <= LAST_DAY(ADD_MONTHS(TO_DATE(:base_yrmn || '01', 'YYYYMMDD'), -1))
올해 -> 내년일 경우
SELECT TO_DATE(DECODE(SUBSTR(:base_yrmn + 1, 5, 2), 13, :base_yrmn + 89, :base_yrmn + 89, :base_yrmn +1) || LPAD(LEVEL, 2, '0'), 'YYYYMMDD'))
FROM DUAL
CONNECT BY ADD_MONTHS(TO_DATE(:base_yrmn || '01', 'YYYYMMDD'), 1) + LEVEL -1 <= LAST_DAY(ADD_MONTHS(TO_DATE(:base_yrmn || '01', 'YYYYMMDD'), 1))
이 부분까지 도출해내면 전월/당월/익월 모든 범위를 표기할 수 있지만, 주차수대로 선별하는 과정에서 예외처리를 상당히 복잡하게 진행해야 한다.
즉 이전년도, 다음년도에 해당하는 일자들이 같이 표기될 경우 일자도출이 불가능하며 다른 도출 방안을 찾아내야 한다.
본 쿼리는 출력대상(표기일자)을 주차수가 아닌 다른 방안을 통해 도출해내는 방법이다(위 방식은 기준일자/전월/익월 모든 일정을 가져온 후에 주차수를 계산하여 표기할 일자들을 선별하였으나 예외처리(년도 바뀔때)가 너무 많고 복잡하다).
기존 방식의 경우 이전/다음년도가 나타나는 상황에서는 사용할 수 없다(주차수 계산이 복잡해짐). 개선한 쿼리에서는 주차수가 아닌 다른 방법으로 표기일자를 도출하며, 속도나 성능 측면에서 보았을때 더 유리할 수 있다.
※ 연초(1월)에 대한 주차수만을 고려하여 제한조건으로 설정할 경우, 연말에 해당 제한조건이 겹칠 수 있기 때문에 정상출력을 진행할 수 없다.
기준일자의 1일(첫번째 요일)을 기준으로, 전월 및 익월까지 넉넉한 범위의 요일을 가져온다(7주씩, 7일로 하여 총 49일을 가져온다)
SELECT
BASE_DATE AS BASE_DATE AS BASE_DATE
,DENSE_RANK() OVER(ORDER BY BASE_DATE) AS SEQ
,TO_CHAR(BASE_DATE 'D') AS DYWK_DTCD
FROM
(
SELECT TO_DATE(:base_yrmn || '01') - TO_NUMBER(TO_CHAR(TO_DATE(:base_yrmn||'01'), 'D')) + 6) + LEVEL AS BASE_DATE
FROM DUAL
CONNECT BY LEVEL <= 7 * 7
)
)
요일의 범위를 가져왔다면, 이를 바탕으로 주차수 등을 계산하여 최종적인 표기일자를 가져온다.
*단, 년도가 넘어갈때의 예외상황을 고려하여 요일 그자체(주차수를 고려하는 것이 아닌, 년도를 포함한 일자로 고려(ORDER BY ASC하면 정렬된 요일들은 반드시 작은 순서부터 고정 출력)하는 것을 의미)로 표기일자를 도출해내도록 한다.
SELECT A.BASE_DATE AS BASE_DATE
,A.DYWK_DTCD AS DYWK_DTCD
,STRN_DATE AS STRN_DATE
,END_DATE AS END_DATE
,CEIL(DENSE_RANK() OVER(ORDER BY BASE_DATE) / 7) AS WKLY_SRNO
FROM WITH_TABLE A
,
(
SELECT MIN(DECODE(DYWK_DTCD, '1', BASE_DATE, '')) AS STRN_DATE
,MAX(DECODE(DYWK_DTCD, '7', BASE_DATE, '')) AS END_DATE
,FROM WITH_TABLE
WHERE DYWK_DTCD IN ('1', '7')
) B
WHERE A.BASE_DATE BETWEEN B.STRN_DATE AND B.END_DATE
sysdate를 TO_CHAR하는 다양한 문법들 - https://goddaehee.tistory.com/242
TO_DATE 문법들 - https://hello-nanam.tistory.com/280