[Oracle] SQL로 달력 만들기: CONNECT BY LEVEL을 활용한 날짜 생성 완벽 가이드
개발을 하다 보면 '데이터가 없는 날짜'까지 포함해서 조회해야 하는 상황을 자주 마주합니다. 예를 들어 일별 매출 리포트를 뽑는데, 매출이 0원인 날짜는 아예 조회되지 않아 그래프가 뚝뚝 끊기는 현상 같은 것들이죠.
이럴 때 필요한 것이 바로 1일부터 말일까지 꽉 채워진 '달력 테이블(Calendar Dummy Table)'입니다. 오늘은 오라클(Oracle)의 강력한 계층형 쿼리 기능인 CONNECT BY LEVEL을 사용하여, 단 3줄의 SQL로 특정 월의 모든 날짜를 생성하는 방법을 심층 분석해 보겠습니다.
1. 핵심 쿼리 분석
가장 먼저, 질문자님께서 공유해주신 코드를 기반으로 핵심 로직을 뜯어보겠습니다. 이 쿼리는 2018년 4월의 1일부터 30일까지를 생성하는 예제입니다.
이 쿼리가 작동하는 원리는 다음과 같습니다.
- (A) DUAL 집합:
CONNECT BY를 수행하기 위한 모태가 되는 1건짜리 데이터입니다. (실제 날짜 값은 바깥에서 하드코딩 되어 있으므로 여기선 단순히 Row Source 역할만 합니다.) - (B) CONNECT BY LEVEL: 오라클에서 행(Row)을 복제하는 가장 효율적인 방법입니다.
LEVEL이라는 의사 컬럼(Pseudo Column)이 1부터 시작하여 조건(이번 달 마지막 날짜)을 만족할 때까지 1씩 증가하며 행을 생성합니다.
💡 결과 데이터 예시
2. 왜 LPAD를 사용했는가? (디테일의 차이)
쿼리를 자세히 보면 LPAD(LEVEL, '2', 0) 함수를 사용하고 있습니다. 날짜 생성 쿼리에서 가장 중요한 부분입니다.
LEVEL은 1, 2, 3... 9, 10 순서로 증가합니다. 만약 LPAD 없이 단순히 문자를 합치면 1일은 '2018041'이 되고, 10일은 '20180410'이 됩니다.
오라클의 TO_DATE 함수는 형식이 맞지 않으면 ORA-01861 에러를 뱉거나 엉뚱한 날짜로 변환해버립니다. 따라서 1~9의 한 자리 숫자를 '01', '09'와 같이 두 자리로 강제 변환해 주는 패딩(Padding) 작업은 필수입니다.
3. [실무 응용] 동적으로 변하는 '이번 달 달력' 만들기
위의 예제는 연월이 고정되어 있습니다. 실무에서는 보통 "현재 월" 혹은 "사용자가 선택한 월"의 전체 날짜 리스트가 필요합니다. 유지보수하기 좋게 개선된 쿼리를 소개합니다.
1. 변수 분리: 인라인 뷰(FROM 절)에서 기준 월(TARGET_MON)을 한 번만 선언하여 쿼리 전체에서 재사용합니다.
2. 정확한 LAST_DAY:
sysdate 대신 입력받은 기준 월을 사용하여, 윤년(2월 29일)이나 월말(30일/31일)을 정확히 계산합니다.3. 요일 추가: 달력 기능 구현 시 필수인 요일(DY) 정보도 함께 추출했습니다.
4. DBA가 알려주는 주의사항: 성능 이슈
CONNECT BY LEVEL은 마법 같은 기능이지만, 잘못 사용하면 DB 성능을 저하시키는 주범이 됩니다.
⚠️ 실제 테이블과 조인 금지 (Cartesian Product)
반드시 DUAL 테이블처럼 1건만 존재하는 테이블을 대상으로 수행해야 합니다. 만약 1만 건의 데이터가 있는 사원 테이블을 대상으로 CONNECT BY LEVEL <= 30을 수행하면, 10,000 * 30 = 300,000건의 데이터가 생성되는 대참사(Cartesian Product)가 발생합니다.
⚠️ 대량 데이터 생성 시 메모리 주의
한 달(30건)이나 1년(365건) 정도는 전혀 문제가 없습니다. 하지만 이 방식으로 수십만 건 이상의 연속된 번호를 생성하려 하면 PGA 메모리를 과도하게 사용하여 성능 저하가 발생할 수 있습니다. 대용량 더미 데이터가 필요할 땐 별도의 숫자 테이블을 관리하는 것이 정석입니다.
5. 마치며 (Bonus Tip)
SQL은 집합적 사고가 필요합니다. 프로그래밍 언어의 for loop 대신, 오라클에서는 CONNECT BY를 통해 집합을 복제하고 확장한다는 개념을 꼭 기억해 주세요.
마지막으로, 이번 달이 며칠까지 있는지 확인하는 가장 간단한 팁을 남겨드립니다. 이 스니펫은 꽤 자주 쓰이니 외워두시면 좋습니다.
댓글
댓글 쓰기