
💡TIP 같이 보기
읽을수 있는 출력을 만들기 위해 SQL에서 다양한 형태의 형식을 사용하기 시작하면
쿼리가 길어집니다. 긴 쿼리에 부담을 갖지 마세요. 여기서 제시하는 쿼리는 한 번
세분화하여 하나씩 실행하면 매우 간단합니다.
WITH x (dy, dm, mth, dw, wk) AS (
SELECT
(CURRENT_DATE - DAY(CURRENT_DATE) DAY + 1 DAY) AS dy,
DAY(CURRENT_DATE - DAY(CURRENT_DATE) DAY + 1 DAY) AS dm,
MONTH(CURRENT_DATE) AS mth,
DAYOFWEEK(CURRENT_DATE - DAY(CURRENT_DATE) DAY + 1 DAY) AS dw,
WEEK_ISO(CURRENT_DATE - DAY(CURRENT_DATE) DAY + 1 DAY) AS wk
FROM t1
UNION ALL
SELECT
dy + 1 DAY,
DAY(dy + 1 DAY),
mth,
DAYOFWEEK(dy + 1 DAY),
WEEK_ISO(dy + 1 DAY)
FROM x
WHERE MONTH(dy + 1 DAY) = mth
)
SELECT
MAX(CASE dw WHEN 2 THEN dm END) AS Mo,
MAX(CASE dw WHEN 3 THEN dm END) AS Tu,
MAX(CASE dw WHEN 4 THEN dm END) AS We,
MAX(CASE dw WHEN 5 THEN dm END) AS Th,
MAX(CASE dw WHEN 6 THEN dm END) AS Fr,
MAX(CASE dw WHEN 7 THEN dm END) AS Sa,
MAX(CASE dw WHEN 1 THEN dm END) AS Su
FROM x
GROUP BY wk
ORDER BY wk;
이 쿼리는 한 달 동안의 날짜를 순회하면서,
각 ISO 주(wk)에 해당하는 월/화/수/목/금/토/일 날짜를 추출하는 쿼리
WITH RECURSIVE x: 한 달 동안의 모든 날짜를 재귀적으로 생성DAYOFWEEK: 요일 (1: 일요일, 2: 월요일, ..., 7: 토요일)GROUP BY wk: ISO 주차별로 묶어서MAX(CASE ...): 요일별 날짜를 열 형태로 추출WITH x AS (
SELECT *
FROM (
SELECT TO_CHAR(TRUNC(SYSDATE, 'MM') + LEVEL - 1, 'IW') wk,
TO_CHAR(TRUNC(SYSDATE, 'MM') + LEVEL - 1, 'DD') dm,
TO_NUMBER(TO_CHAR(TRUNC(SYSDATE, 'MM') + LEVEL - 1, 'D')) dw,
TO_CHAR(TRUNC(SYSDATE, 'MM') + LEVEL - 1, 'MM') curr_mth,
TO_CHAR(SYSDATE, 'MM') mth
FROM dual
CONNECT BY LEVEL <= 31
)
WHERE curr_mth = mth
)
SELECT
MAX(CASE dw WHEN 2 THEN dm END) Mo,
MAX(CASE dw WHEN 3 THEN dm END) Tu,
MAX(CASE dw WHEN 4 THEN dm END) We,
MAX(CASE dw WHEN 5 THEN dm END) Th,
MAX(CASE dw WHEN 6 THEN dm END) Fr,
MAX(CASE dw WHEN 7 THEN dm END) Sa,
MAX(CASE dw WHEN 1 THEN dm END) Su
FROM x
GROUP BY wk
ORDER BY wk;
| 함수 | 역할 |
|---|---|
TRUNC(SYSDATE, 'MM') | 이번 달 1일로 자름 |
LEVEL | 1 ~ 31 생성 (날짜 반복) |
TO_CHAR(..., 'IW') | ISO 주차 반환 |
TO_CHAR(..., 'DD') | 2자리 일 반환 |
TO_CHAR(..., 'D') | 요일 숫자 반환 (1=일, 2=월...) |
MAX(CASE ...) | 요일별 컬럼을 세로 → 가로로 피벗함 |
SELECT max(case dw when 2 then dm end) as Mo,
max(case dw when 3 then dm end) as Tu,
max(case dw when 4 then dm end) as We,
max(case dw when 5 then dm end) as Th,
max(case dw when 6 then dm end) as Fr,
max(case dw when 7 then dm end) as Sa,
max(case dw when 1 then dm end) as Su
FROM (
SELECT *
FROM (
SELECT CAST(date_trunc('month', current_date) AS date) + x.id,
to_char(
CAST(date_trunc('month', current_date) AS date) + x.id, 'iw'
) as wk,
to_char(
CAST(date_trunc('month', current_date) AS date) + x.id, 'dd'
) as dm,
CAST(
to_char(
CAST(date_trunc('month', current_date) AS date) + x.id, 'd'
) AS integer
) as dw,
to_char(
CAST(date_trunc('month', current_date) AS date) + x.id, 'mm'
) as curr_mth,
to_char(current_date, 'mm') as mth
FROM generate_series(0, 31) x(id)
) x
WHERE mth = curr_mth
) y
GROUP BY wk
ORDER BY wk;
FROM generate_series(0, 31) x(id)CAST(date_trunc('month', current_date) AS date) + x.iddate_trunc('month', current_date) → 이번 달 1일id를 더해 → 이번 달의 날짜 목록 생성to_char(..., 'iw') as wk'2024-04-01' → '14' (14번째 주)to_char(..., 'dd') as dm'2024-04-01' → '01'to_char(..., 'd') → cast(... as integer) as dwto_char(..., 'mm') as curr_mth'04', '05' 등)to_char(current_date, 'mm') as mthWHERE mth = curr_mthx로 감싸고,y로 감싸서 group by에 활용하기 위한 준비 sql
복사편집
SELECT
max(case dw when 2 then dm end) as Mo,
max(case dw when 3 then dm end) as Tu,
max(case dw when 4 then dm end) as We,
max(case dw when 5 then dm end) as Th,
max(case dw when 6 then dm end) as Fr,
max(case dw when 7 then dm end) as Sa,
max(case dw when 1 then dm end) as Su
dw가 2면 → 월요일dw가 3이면 → 화요일 ...GROUP BY wkiw) 기준으로 묶음ORDER BY wkWITH RECURSIVE x(dy, dm, mth, dw, wk) AS (
SELECT dy,
DAY(dy),
MONTH(dy),
DAYOFWEEK(dy),
CASE
WHEN DAYOFWEEK(dy) = 1 THEN WEEK(dy) - 1
ELSE WEEK(dy)
END
FROM (
SELECT DATE_ADD(CURDATE(), INTERVAL -DAY(CURDATE()) + 1 DAY) dy
FROM t1
) x
UNION ALL
SELECT DATE_ADD(dy, INTERVAL 1 DAY),
DAY(DATE_ADD(dy, INTERVAL 1 DAY)),
mth,
DAYOFWEEK(DATE_ADD(dy, INTERVAL 1 DAY)),
CASE
WHEN DAYOFWEEK(DATE_ADD(dy, INTERVAL 1 DAY)) = 1
THEN WEEK(DATE_ADD(dy, INTERVAL 1 DAY)) - 1
ELSE WEEK(DATE_ADD(dy, INTERVAL 1 DAY))
END
FROM x
WHERE MONTH(DATE_ADD(dy, INTERVAL 1 DAY)) = mth
)
SELECT MAX(CASE dw WHEN 2 THEN dm END) Mo,
MAX(CASE dw WHEN 3 THEN dm END) Tu,
MAX(CASE dw WHEN 4 THEN dm END) We,
MAX(CASE dw WHEN 5 THEN dm END) Th,
MAX(CASE dw WHEN 6 THEN dm END) Fr,
MAX(CASE dw WHEN 7 THEN dm END) Sa,
MAX(CASE dw WHEN 1 THEN dm END) Su
FROM x
GROUP BY wk
ORDER BY wk;
FROM t1t1)은 쿼리를 실행시키기 위한 목적DATE_ADD(CURDATE(), INTERVAL -DAY(CURDATE()) + 1 DAY)2024-04-15 → 2024-04-01WITH RECURSIVE x(...) AS (...)SELECT dy, DAY(dy), MONTH(dy), DAYOFWEEK(dy)dy: 날짜 자체dm: 일(day)mth: 월(month)dw: 요일 (1: 일요일 ~ 7: 토요일)CASE WHEN DAYOFWEEK(dy) = 1 THEN WEEK(dy) - 1 ELSE WEEK(dy)WEEK)를 하나 줄여서 정렬 시 ISO 주차처럼 맞춤UNION ALL ... WHERE MONTH(DATE_ADD(...)) = mth바깥 SELECTSELECT MAX(CASE dw WHEN ... THEN dm END)
dw = 2~7, 1 조건에 맞는 dm(일자) 값을 꺼냄MAX()는 각 주차(wk) 안에서 요일별 일자 1개만 추출하려는 목적GROUP BY wkORDER BY wkWITH x(dy, dm, mth, dw, wk) AS (
SELECT dy,
DAY(dy),
DATEPART(m, dy),
DATEPART(dw, dy),
CASE
WHEN DATEPART(dw, dy) = 1 THEN DATEPART(ww, dy) - 1
ELSE DATEPART(ww, dy)
END
FROM (
SELECT DATEADD(day, -DAY(GETDATE()) + 1, GETDATE()) dy
FROM t1
) x
UNION ALL
SELECT DATEADD(d, 1, dy),
DAY(DATEADD(d, 1, dy)),
mth,
DATEPART(dw, DATEADD(d, 1, dy)),
CASE
WHEN DATEPART(dw, DATEADD(d, 1, dy)) =
1 THEN DATEPART(wk, DATEADD(d, 1, dy)) - 1
ELSE DATEPART(wk, DATEADD(d, 1, dy))
END
FROM x
WHERE DATEPART(m, DATEADD(d, 1, dy)) = mth
)
SELECT MAX(CASE dw WHEN 2 THEN dm END) as Mo,
MAX(CASE dw WHEN 3 THEN dm END) as Tu,
MAX(CASE dw WHEN 4 THEN dm END) as We,
MAX(CASE dw WHEN 5 THEN dm END) as Th,
MAX(CASE dw WHEN 6 THEN dm END) as Fr,
MAX(CASE dw WHEN 7 THEN dm END) as Sa,
MAX(CASE dw WHEN 1 THEN dm END) as Su
FROM x
GROUP BY wk
ORDER BY wk;
DATEADD(day, -DAY(GETDATE()) + 1, GETDATE())2024-04-13 → 2024-04-01WITH x(...)dy: 날짜 자체dm: 일(day)mth: 월(month)dw: 요일 (1: 일요일 ~ 7: 토요일)wk: 주차 (단, 일요일이면 1을 빼서 ISO 주처럼 조정UNION ALL ... WHERE DATEPART(m, ...) = mthSELECT MAX(CASE dw WHEN 2 THEN dm END) as Mo,
MAX(CASE dw WHEN 3 THEN dm END) as Tu,
MAX(CASE dw WHEN 4 THEN dm END) as We,
MAX(CASE dw WHEN 5 THEN dm END) as Th,
MAX(CASE dw WHEN 6 THEN dm END) as Fr,
MAX(CASE dw WHEN 7 THEN dm END) as Sa,
MAX(CASE dw WHEN 1 THEN dm END) as Su
FROM x
GROUP BY wk
ORDER BY wkdw(요일)에 따라 dm(일자)을 가로로 정렬MAX()는 각 주차 내에서 요일에 해당하는 날짜 하나만 추출GROUP BY wkORDER BY wk