기본 콘텐츠로 건너뛰기

Oracle Calendar

[Oracle] SQL로 달력 만들기: CONNECT BY LEVEL을 활용한 날짜 생성 완벽 가이드

개발을 하다 보면 '데이터가 없는 날짜'까지 포함해서 조회해야 하는 상황을 자주 마주합니다. 예를 들어 일별 매출 리포트를 뽑는데, 매출이 0원인 날짜는 아예 조회되지 않아 그래프가 뚝뚝 끊기는 현상 같은 것들이죠.

이럴 때 필요한 것이 바로 1일부터 말일까지 꽉 채워진 '달력 테이블(Calendar Dummy Table)'입니다. 오늘은 오라클(Oracle)의 강력한 계층형 쿼리 기능인 CONNECT BY LEVEL을 사용하여, 단 3줄의 SQL로 특정 월의 모든 날짜를 생성하는 방법을 심층 분석해 보겠습니다.

1. 핵심 쿼리 분석

가장 먼저, 질문자님께서 공유해주신 코드를 기반으로 핵심 로직을 뜯어보겠습니다. 이 쿼리는 2018년 4월의 1일부터 30일까지를 생성하는 예제입니다.

SELECT TO_CHAR(TO_DATE('201804'||LPAD(LEVEL,'2',0), 'YYYYMMDD'),'YYYYMMDD') AS YMD , LEVEL FROM (SELECT TO_DATE('20140601', 'YYYYMMDD') MAKE_DATES FROM DUAL) -- (A) CONNECT BY LEVEL <= TO_CHAR(LAST_DAY(sysdate),'DD') -- (B)

이 쿼리가 작동하는 원리는 다음과 같습니다.

  • (A) DUAL 집합: CONNECT BY를 수행하기 위한 모태가 되는 1건짜리 데이터입니다. (실제 날짜 값은 바깥에서 하드코딩 되어 있으므로 여기선 단순히 Row Source 역할만 합니다.)
  • (B) CONNECT BY LEVEL: 오라클에서 행(Row)을 복제하는 가장 효율적인 방법입니다. LEVEL이라는 의사 컬럼(Pseudo Column)이 1부터 시작하여 조건(이번 달 마지막 날짜)을 만족할 때까지 1씩 증가하며 행을 생성합니다.

💡 결과 데이터 예시

20180401 1 20180402 2 ... (중략) ... 20180430 30

2. 왜 LPAD를 사용했는가? (디테일의 차이)

쿼리를 자세히 보면 LPAD(LEVEL, '2', 0) 함수를 사용하고 있습니다. 날짜 생성 쿼리에서 가장 중요한 부분입니다.

'201804' || 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. [실무 응용] 동적으로 변하는 '이번 달 달력' 만들기

위의 예제는 연월이 고정되어 있습니다. 실무에서는 보통 "현재 월" 혹은 "사용자가 선택한 월"의 전체 날짜 리스트가 필요합니다. 유지보수하기 좋게 개선된 쿼리를 소개합니다.

SELECT TO_CHAR(TO_DATE(TARGET_MON || LPAD(LEVEL, 2, '0'), 'YYYYMMDD'), 'YYYYMMDD') AS DATE_YMD , TO_CHAR(TO_DATE(TARGET_MON || LPAD(LEVEL, 2, '0'), 'YYYYMMDD'), 'DY') AS DAY_OF_WEEK FROM ( SELECT '202512' AS TARGET_MON FROM DUAL -- 여기에 원하는 월(YYYYMM) 입력 ) CONNECT BY LEVEL <= TO_CHAR(LAST_DAY(TO_DATE(TARGET_MON, 'YYYYMM')), 'DD')
👉 개선 포인트
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를 통해 집합을 복제하고 확장한다는 개념을 꼭 기억해 주세요.

마지막으로, 이번 달이 며칠까지 있는지 확인하는 가장 간단한 팁을 남겨드립니다. 이 스니펫은 꽤 자주 쓰이니 외워두시면 좋습니다.

-- 이번 달의 마지막 일자 숫자만 구하기 (예: 31) SELECT TO_CHAR(LAST_DAY(SYSDATE), 'DD') FROM DUAL;
#Oracle #SQL #ConnectByLevel #계층형쿼리 #오라클달력 #더미데이터 #SQL튜닝 #Database

댓글

이 블로그의 인기 게시물

Java Servlet Request Parameter 완전 정복 — GET/POST 모든 파라미터 확인 & 디버깅 예제 (Request Parameter 전체보기)

Java Servlet Request Parameter 완전 정복 — GET/POST 모든 파라미터 확인 & 디버깅 예제 Java Servlet Request Parameter 완전 정복 웹 애플리케이션에서 클라이언트로부터 전달되는 Request Parameter 를 확인하는 것은 필수입니다. 이 글에서는 Java Servlet 과 JSP 에서 GET/POST 요청 파라미터를 전체 출력하고 디버깅하는 방법을 다양한 예제와 함께 소개합니다. 1. 기본 예제: getParameterNames() 사용 Enumeration<String> params = request.getParameterNames(); System.out.println("----------------------------"); while (params.hasMoreElements()){ String name = params.nextElement(); System.out.println(name + " : " + request.getParameter(name)); } System.out.println("----------------------------"); 위 코드는 요청에 포함된 모든 파라미터 이름과 값을 출력하는 기본 방법입니다. 2. HTML Form과 연동 예제 <form action="CheckParamsServlet" method="post"> 이름: <input type="text" name="username"><br> 이메일: <input type="email" name="email"><b...

PostgreSQL 달력(일별,월별)

SQL 팁: GENERATE_SERIES로 일별, 월별 날짜 목록 만들기 SQL 팁: GENERATE_SERIES 로 일별, 월별 날짜 목록 만들기 데이터베이스에서 통계 리포트를 작성하거나 비어있는 날짜 데이터를 채워야 할 때, 특정 기간의 날짜 목록이 필요할 수 있습니다. PostgreSQL과 같은 데이터베이스에서는 GENERATE_SERIES 함수를 사용하여 이 작업을 매우 간단하게 처리할 수 있습니다. 1. 🗓️ 일별 날짜 목록 생성하기 2020년 1월 1일부터 12월 31일까지의 모든 날짜를 '1 day' 간격으로 생성하는 쿼리입니다. WITH date_series AS ( SELECT DATE(GENERATE_SERIES( TO_DATE('2020-01-01', 'YYYY-MM-DD'), TO_DATE('2020-12-31', 'YYYY-MM-DD'), '1 day' )) AS DATE ) SELECT DATE FROM date_series 이 쿼리는 WITH 절(CTE)을 사용하여 date_series 라는 임시 테이블을 만들고, GENERATE_SERIES 함수로 날짜를 채웁니다. 결과 (일별 출력) 2. 📅 월별 날짜 목록 생성하기 동일한 원리로, 간격을 '1 MONTH' 로 변경하면 월별 목록을 생성할 수 있습니다. TO...

CSS로 레이어 팝업 화면 가운데 정렬하는 방법 (top·left·transform 완전 정리)

레이어 팝업 센터 정렬, 이 코드만 알면 끝 (CSS 예제 포함) 이벤트 배너나 공지사항을 띄울 때 레이어 팝업(center 정렬) 을 깔끔하게 잡는 게 생각보다 어렵습니다. 화면 크기가 변해도 가운데에 고정되고, 모바일에서도 자연스럽게 보이게 하려면 position , top , left , transform 을 정확하게 이해해야 합니다. 이 글에서는 아래 내용을 예제로 정리합니다. 레이어 팝업(center 정렬)의 기본 개념 자주 사용하는 position: absolute / fixed 정렬 방식 질문에서 주신 스타일 top: 3.25%; left: 50%; transform: translateX(-50%) 의 의미 실무에서 바로 쓰는 반응형 레이어 팝업 HTML/CSS 예제 1. 레이어 팝업(center 정렬)이란? 레이어 팝업(레이어 팝업창) 은 새 창을 띄우는 것이 아니라, 현재 페이지 위에 div 레이어를 띄워서 공지사항, 광고, 이벤트 등을 보여주는 방식을 말합니다. 검색엔진(SEO) 입장에서도 같은 페이지 안에 HTML이 존재 하기 때문에 팝업 안의 텍스트도 정상적으로 인덱싱될 수 있습니다. 즉, “레이어 팝업 센터 정렬”, “레이어 팝업 만드는 방법”과 같이 관련 키워드를 적절히 넣어주면 검색 노출에 도움이 됩니다. 2. 질문에서 주신 레이어 팝업 스타일 분석 질문에서 주신 스타일은 다음과 같습니다. <div class="layer-popup" style="width:1210px; z-index:9001; position:absolute; top:3.25%; left:50%; transform:translateX(-50%);"> 레이어 팝업 내용 <...