09장. 날짜 조작 기법
🎨 DB2
- SUBSTR 함수를 사용하여 인라인 뷰 X에서 연도를 반환합니다.
- MOD 함수를 사용하여 분기를 결정합니다.
SELECT (q_end - 2 MONTH) q_start,
(q_end + 1 MONTH) - 1 DAY q_end
FROM (
SELECT DATE(
SUBSTR(CAST(yrq AS CHAR(4)), 1, 4) || '-' ||
RTRIM(CAST(MOD(yrq, 10) * 3 AS CHAR(2))) || '-1'
) q_end
FROM (
SELECT 20051 yrq FROM t1
UNION ALL
SELECT 20052 yrq FROM t1
UNION ALL
SELECT 20053 yrq FROM t1
UNION ALL
SELECT 20054 yrq FROM t1
) x
) y
✅ 해석
-
내부 서브쿼리 x
SELECT 20051 AS yrq
UNION ALL SELECT 20052
UNION ALL SELECT 20053
UNION ALL SELECT 20054
- 분기 정보를 의미하는 숫자 코드
yrq를 나열
yrq는 YYYYQ 형식으로 구성됨
- 예:
20051 → 2005년 1분기 (Q1)
-
MOD(yrq, 10) * 3
yrq에서 분기 숫자 추출 후 3을 곱해 해당 분기의 마지막 달 계산
- Q1 → 1 × 3 = 3 (3월)
- Q2 → 2 × 3 = 6 (6월)
- Q3 → 3 × 3 = 9 (9월)
- Q4 → 4 × 3 = 12 (12월)
-
SUBSTR(...), || '-' || ..., || '1'
- 문자열을 연결하여 날짜 형식의 문자열
'YYYY-MM-1' 생성
- 예:
20052 → "2005-6-1"
- 이는 해당 분기 종료월의 첫째 날을 의미
-
DATE(...) → q_end
- 문자열을 날짜 형식으로 변환
q_end는 해당 분기 종료월의 1일
-
바깥 SELECT
SELECT
(q_end - INTERVAL 2 MONTH) AS q_start,
(q_end + INTERVAL 1 MONTH - INTERVAL 1 DAY) AS q_end
q_start: q_end 기준 2개월 전 → 분기 시작일
q_end: q_end의 다음 달 첫날에서 하루를 빼서 → 분기 종료일
예시:
q_end = 2005-04-01이면
q_start = 2005-02-01
q_end = 2005-04-30
- 따라서 실제 분기 기간:
2005-01-01 ~ 2005-03-31
✅ 결과 예시
| q_start | q_end |
|---|
| 2005-01-01 | 2005-03-31 |
| 2005-04-01 | 2005-06-30 |
| 2005-07-01 | 2005-09-30 |
| 2005-10-01 | 2005-12-31 |
🎨 Oracle
- SUBSTR함수를 사용하여 인라인 뷰 X에서 연도를 반환합니다.
- MOD 함수를 사용하여 분기를 결정합ㄴ디ㅏ.
SELECT ADD_MONTHS(q_end, -2) q_start,
LAST_DAY(q_end) q_end
FROM (
SELECT TO_DATE(SUBSTR(yrq, 1, 4) || MOD(yrq, 10) * 3, 'YYYYMM') q_end
FROM (
SELECT 20051 yrq FROM dual
UNION ALL
SELECT 20052 yrq FROM dual
UNION ALL
SELECT 20053 yrq FROM dual
UNION ALL
SELECT 20054 yrq FROM dual
) x
) y
✅ 해석
-
SELECT 20051, 20052, ..., 20054 FROM dual
- 분기 코드(
yrq)를 나열
- 코드 구조:
YYYYQ
20051 → 2005년 1분기
20054 → 2005년 4분기
-
MOD(yrq, 10) * 3
yrq에서 분기 숫자 추출 후 3을 곱해 해당 분기의 종료 월 계산
- Q1 → 1 × 3 = 3 → 3월
- Q2 → 2 × 3 = 6 → 6월
- Q3 → 3 × 3 = 9 → 9월
- Q4 → 4 × 3 = 12 → 12월
-
SUBSTR(yrq, 1, 4) || ... → 'YYYYMM'
- 문자열 결합:
- 연도 부분:
SUBSTR(yrq, 1, 4) → "2005"
- 종료 월:
MOD(...) * 3 → "06"
- 예:
20052 → '200506'
- 결과: 해당 분기의 종료 월을 나타내는 문자열
-
TO_DATE(..., 'YYYYMM')
- 문자열
'YYYYMM'을 날짜형(YYYY-MM-01) 으로 변환
- 예:
'200506' → 2005-06-01
-
q_end = 종료 월의 1일
- 위
TO_DATE 결과를 q_end로 사용
- 예:
q_end = 2005-06-01
-
ADD_MONTHS(q_end, -2) → 분기 시작일
q_end에서 2개월을 빼면 분기 시작월의 1일
- 예:
2005-06-01 → 2005-04-01
- ✅ 출력 컬럼:
q_start
-
LAST_DAY(q_end) → 분기 종료일
- 종료 월의 마지막 날을 계산
- 예:
LAST_DAY(2005-06-01) → 2005-06-30
- ✅ 출력 컬럼:
q_end
✅ 결과 예시
| q_start | q_end |
|---|
| 2005-01-01 | 2005-03-31 |
| 2005-04-01 | 2005-06-30 |
| 2005-07-01 | 2005-09-30 |
| 2005-10-01 | 2005-12-31 |
🎨 PostgreSQL
- SUBSTR 함수를 사용하여 인라인 뷰 X 에서 연도를 반환합니다.
- MOD 함수를 사용하여 분기를 결정합니다.
SELECT DATE(q_end - (2 * INTERVAL '1 month')) q_start,
DATE(q_end + INTERVAL '1 month' - INTERVAL '1 day') q_end
FROM (
SELECT TO_DATE(SUBSTR(yrq, 1, 4) || MOD(yrq, 10) * 3, 'YYYYMM') q_end
FROM (
SELECT 20051 yrq FROM t1
UNION ALL
SELECT 20052 yrq FROM t1
UNION ALL
SELECT 20053 yrq FROM t1
UNION ALL
SELECT 20054 yrq FROM t1
) x
) y
✅ 해석
-
내부 쿼리: SELECT 20051, ..., 20054 FROM t1
yrq 형식의 분기 코드 나열
- 예:
20051 = 2005년 1분기, 20054 = 2005년 4분기
-
MOD(yrq, 10) * 3`
- 분기 번호 추출 후 종료 월 계산
- Q1 → 1 × 3 = 3 (3월)
- Q2 → 2 × 3 = 6 (6월)
- Q3 → 3 × 3 = 9 (9월)
- Q4 → 4 × 3 = 12 (12월)
-
SUBSTR(yrq, 1, 4) || ... → 'YYYYMM'
- 연도(
YYYY)와 분기 종료 월(MM)을 문자열로 결합
-
TO_DATE(..., 'YYYYMM') → q_end
- 해당 문자열을 날짜로 변환 → 종료 월의 1일
-
바깥 SELECT
DATE(q_end - (2 * INTERVAL '1 month')) AS q_start,
DATE(q_end + INTERVAL '1 month' - INTERVAL '1 day') AS q_end
q_start: 종료 월 기준 -2개월 → 분기 시작일
- 예:
2005-06-01 - 2개월 → 2005-04-01
q_end: 종료 월 기준 +1개월 후 하루 전 → 분기 종료일
- 예:
2005-06-01 + 1개월 - 1일 → 2005-06-30
✅ 결과 예시
| q_start | q_end |
|---|
| 2005-01-01 | 2005-03-31 |
| 2005-04-01 | 2005-06-30 |
| 2005-07-01 | 2005-09-30 |
| 2005-10-01 | 2005-12-31 |
🎨 MySQL
- SUBSTR 함수를 사용하여 인라인 뷰 X 에서 연도를 반환합니다.
- MOD 함수를 사용하여 분기를 결정합니다.
SELECT DATE_ADD(
ADDDATE(q_end, -DAY(q_end) + 1),
INTERVAL -2 MONTH
) q_start,
q_end
FROM (
SELECT LAST_DAY(
STR_TO_DATE(
CONCAT(
SUBSTR(yrq, 1, 4),
LPAD(MOD(yrq, 10) * 3, 2, '0')
),
'%Y%m'
)
) q_end
FROM (
SELECT 20051 yrq FROM t1
UNION ALL
SELECT 20052 yrq FROM t1
UNION ALL
SELECT 20053 yrq FROM t1
UNION ALL
SELECT 20054 yrq FROM t1
) x
) y;
✅ 해석
-
내부 서브쿼리
SELECT 20051, 20052, 20053, 20054 FROM t1
-
MOD(yrq, 10) * 3
- 분기 번호(1~4)를 추출한 뒤 3을 곱해 해당 분기의 종료 월 계산
- Q1 → 3, Q2 → 6, Q3 → 9, Q4 → 12
LPAD(..., 2, '0')를 사용해 '06', '09'처럼 두 자리 문자열로 보정
-
STR_TO_DATE(..., '%Y%m')
-
LAST_DAY(...) → q_end
- 해당 월의 마지막 날을 구함
- 예:
2005-06-01 → 2005-06-30
-
바깥 SELECT
DATE_ADD(ADDDATE(q_end, -DAY(q_end) + 1), INTERVAL -2 MONTH) AS q_start,
q_end
DAY(q_end) → 종료일의 "일" 숫자 추출 (예: 30)
q_end - 일자 + 1 → 해당 월의 1일 (예: 6월 30일 → 6월 1일)
- 그로부터 2개월 전 → 분기 시작일
- 예:
2005-06-01 - 2개월 → 2005-04-01
결과:
q_start = 2005-04-01
q_end = 2005-06-30
✅ 결과 예시
| q_start | q_end |
|---|
| 2005-01-01 | 2005-03-31 |
| 2005-04-01 | 2005-06-30 |
| 2005-07-01 | 2005-09-30 |
| 2005-10-01 | 2005-12-31 |
🎨 SQL Server
- SUBSTRING 함수를 사용하여 인라인 뷰 X에서 연도를 반환합니다.
- 절댓값 함수(%)를 사용하여 찾고 있는 분기를 결정합니다.
SELECT DATEADD(m, -2, q_end) q_start,
DATEADD(d, -1, DATEADD(m, 1, q_end)) q_end
FROM (
SELECT CAST(
SUBSTRING(CAST(yrq AS VARCHAR), 1, 4) + '-' +
CAST(yrq % 10 * 3 AS VARCHAR) + '-1'
AS DATETIME) q_end
FROM (
SELECT 20051 yrq FROM t1
UNION ALL
SELECT 20052 yrq FROM t1
UNION ALL
SELECT 20053 yrq FROM t1
UNION ALL
SELECT 20054 yrq FROM t1
) x
) y;
✅ 해석
-
SELECT 20051~20054 AS yrq FROM t1
yrq는 분기 코드를 의미하는 값 (형식: YYYYQ)
-
yrq % 10 * 3
yrq에서 마지막 자리를 이용해 분기 숫자(1~4) 추출
- 여기에 3을 곱해 종료 월 계산
- Q1 → 3월, Q2 → 6월, Q3 → 9월, Q4 → 12월
-
SUBSTRING(...) + '-' + ... + '-1'
yrq를 문자열로 변환하여 'YYYY-M-1' 형식 생성
- 이는 종료 월의 1일을 의미하는 문자열
-
CAST(... AS DATETIME) → q_end
- 위 문자열을 날짜형(DATETIME) 으로 변환
q_end는 해당 분기의 종료 월의 1일
-
바깥 SELECT
DATEADD(m, -2, q_end) AS q_start,
DATEADD(d, -1, DATEADD(m, 1, q_end)) AS q_end
q_start: q_end 기준 2개월 전 → 분기 시작일
- 예:
2005-06-01 - 2개월 = 2005-04-01
q_end: q_end 기준 +1개월 후 -1일 → 분기 종료일
- 예:
2005-06-01 + 1개월 - 1일 = 2005-06-30
✅ 예시 결과
| q_start | q_end |
|---|
| 2005-01-01 | 2005-03-31 |
| 2005-04-01 | 2005-06-30 |
| 2005-07-01 | 2005-09-30 |
| 2005-10-01 | 2005-12-31 |