소스: sql-src/batch_05_partition/01_manual_split.sql, 02_key_choice.sql, 03_drop_vs_delete.sql.
이 절의 sql 은 모두 H2 로 실행한 평문 SQL 입니다. 월별 표 여러 개와 UNION ALL 뷰로 파티션처럼 나눈 구조를 흉내 낸 것이며, 실제 파티션이 아닙니다. 실제 파티션은 표 하나를 DB 가 알아서 나누고, 여기서는 사람이 표를 나눠 놓고 뷰로 묶습니다.
1월부터 4월까지 월별 표를 만들고 뷰로 묶습니다. 표마다 그 달의 날짜만 들어 있습니다. CREATE TABLE 과 INSERT 는 네 표가 같은 형태이며 행 수만 다릅니다(1월 1,000, 2월 800, 3월 1,200, 4월 500).
CREATE VIEW orders_all AS
SELECT * FROM orders_2025_01
UNION ALL
SELECT * FROM orders_2025_02
UNION ALL
SELECT * FROM orders_2025_03
UNION ALL
SELECT * FROM orders_2025_04;표별 건수와 날짜 범위를 확인합니다.
SELECT
'2025_01' AS part
, COUNT(*) AS cnt
, MIN(ord_date) AS min_date
, MAX(ord_date) AS max_date
FROM orders_2025_01
UNION ALL
SELECT '2025_02', COUNT(*), MIN(ord_date), MAX(ord_date) FROM orders_2025_02
UNION ALL
SELECT '2025_03', COUNT(*), MIN(ord_date), MAX(ord_date) FROM orders_2025_03
UNION ALL
SELECT '2025_04', COUNT(*), MIN(ord_date), MAX(ord_date) FROM orders_2025_04;PART | CNT | MIN_DATE | MAX_DATE
--------+------+------------+-----------
2025_01 | 1000 | 2025-01-01 | 2025-01-31
2025_02 | 800 | 2025-02-01 | 2025-02-28
2025_03 | 1200 | 2025-03-01 | 2025-03-31
2025_04 | 500 | 2025-04-01 | 2025-04-30
(4행)각 표의 최소·최대 날짜가 그 달 안에 들어 있습니다. 이 범위가 파티션의 경계 값과 같은 역할을 합니다. 뷰로 전체를 세면 네 표의 합입니다.
SELECT COUNT(*) AS total_rows FROM orders_all;TOTAL_ROWS
----------
3500
(1행)3월 조건으로 뷰를 조회한 결과와, 3월 표만 직접 조회한 결과를 비교합니다.
SELECT
COUNT(*) AS cnt
, SUM(amt) AS sum_amt
FROM orders_all
WHERE ord_date >= DATE '2025-03-01'
AND ord_date < DATE '2025-04-01';CNT | SUM_AMT
-----+--------
1200 | 5773800
(1행)SELECT
COUNT(*) AS cnt
, SUM(amt) AS sum_amt
FROM orders_2025_03;CNT | SUM_AMT
-----+--------
1200 | 5773800
(1행)두 결과가 같습니다. 3월 조건에 맞는 행은 3월 표에만 있으므로 나머지 세 표를 읽지 않아도 답이 같습니다. 이것이 프루닝의 원리입니다. 실제 파티션은 DB 가 이 판단을 자동으로 하고, 이 뷰는 그 판단을 사람이 대신하는 흉내입니다.
조건에 걸리는 행이 표마다 몇 개인지 세면 어느 표를 건너뛸 수 있는지 보입니다.
SELECT
part
, cnt
FROM (
SELECT '2025_01' AS part, COUNT(*) AS cnt FROM orders_2025_01 WHERE ord_date >= DATE '2025-03-01' AND ord_date < DATE '2025-04-01'
UNION ALL
SELECT '2025_02', COUNT(*) FROM orders_2025_02 WHERE ord_date >= DATE '2025-03-01' AND ord_date < DATE '2025-04-01'
UNION ALL
SELECT '2025_03', COUNT(*) FROM orders_2025_03 WHERE ord_date >= DATE '2025-03-01' AND ord_date < DATE '2025-04-01'
UNION ALL
SELECT '2025_04', COUNT(*) FROM orders_2025_04 WHERE ord_date >= DATE '2025-03-01' AND ord_date < DATE '2025-04-01'
) t
ORDER BY part;PART | CNT
--------+-----
2025_01 | 0
2025_02 | 0
2025_03 | 1200
2025_04 | 0
(4행)3월 표만 건수가 있고 나머지는 0 입니다. 0 인 표는 읽어도 얻는 것이 없는 표이고, 프루닝은 이런 표를 미리 건너뜁니다. 이 쿼리 자체는 모든 표를 읽었으므로 프루닝의 예가 아니라 어느 표가 건너뛸 대상인지 보이는 예입니다.
같은 4,000행을 세 가지 키로 나눌 때 조각 크기가 어떻게 되는지 계산합니다. 범위·목록·해시가 각각 어떤 분포를 만드는지 보는 것입니다. 데이터는 orders 4,000행이고 날짜는 1월부터 4월까지, 지점은 다섯 곳입니다.
범위 키는 날짜의 월입니다.
SELECT
EXTRACT(MONTH FROM ord_date) AS mon
, COUNT(*) AS cnt
FROM orders
GROUP BY EXTRACT(MONTH FROM ord_date)
ORDER BY mon;MON | CNT
----+-----
1 | 1053
2 | 934
3 | 1023
4 | 990
(4행)월별로 크기가 조금씩 다릅니다. 달마다 일수와 데이터량이 달라 완전히 같을 수는 없지만 비슷한 크기입니다.
목록 키는 지점입니다.
SELECT
branch
, COUNT(*) AS cnt
FROM orders
GROUP BY branch
ORDER BY cnt DESC;BRANCH | CNT
-------+-----
서울 | 2000
부산 | 1000
대구 | 600
광주 | 200
제주 | 200
(5행)서울이 절반입니다. 목록 파티션은 값이 몰리면 조각 크기가 크게 달라집니다. 서울 조각만 커서 조각 나누기의 효과가 줄어듭니다.
해시 키는 id 를 4 로 나눈 나머지입니다.
SELECT
MOD(id, 4) AS bucket
, COUNT(*) AS cnt
FROM orders
GROUP BY MOD(id, 4)
ORDER BY bucket;BUCKET | CNT
-------+-----
0 | 1000
1 | 1000
2 | 1000
3 | 1000
(4행)네 조각이 정확히 같습니다. 해시는 고르게 나누는 데 강합니다. 대신 값이 어느 조각에 있는지가 해시로 정해져, 기간 조회 같은 범위 조건으로는 조각을 줄일 수 없습니다. 해시 키는 보통 특정 값 하나를 찾는 조회에 맞습니다.
1월 데이터를 지우는 두 방법을 봅니다. 큰 표 하나에서 DELETE 로 지우는 방법과, 월별 표에서 그 달 표를 비우는 방법입니다. 결과 건수만 비교하고 속도는 측정하지 않았습니다.
DELETE FROM orders_big WHERE ord_date < DATE '2025-02-01';
SELECT
COUNT(*) AS big_rows
, MIN(ord_date) AS min_date
FROM orders_big;BIG_ROWS | MIN_DATE
---------+-----------
2947 | 2025-02-01
(1행)4,000행에서 1월 1,053행이 지워졌고 남은 최소 날짜는 2월 1일입니다. DELETE 는 조건에 맞는 행을 찾아 한 행씩 지우고 로그를 남기고 인덱스도 고칩니다. 행이 수억이면 이 작업이 오래 걸립니다.
월별 표에서는 1월 표를 비우면 끝납니다.
TRUNCATE TABLE orders_2025_01;
SELECT COUNT(*) AS jan_rows FROM orders_2025_01;JAN_ROWS
--------
0
(1행)TRUNCATE 는 행을 찾지 않고 표의 데이터 공간을 통째로 비웁니다. 지울 행이 몇 개인지와 상관없이 일이 작습니다. 이것이 파티션 단위 관리의 이득입니다. 2월 표는 DROP 으로 표째 없앨 수도 있습니다.
DROP TABLE orders_2025_02;
SELECT COUNT(*) AS tables_left FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'ORDERS_2025_02';TABLES_LEFT
-----------
0
(1행)참고이 비교에서 두 방법의 결과 건수만 확인했습니다. 실제 시간 차이는 행 수, 인덱스, 로그 설정에 따라 크게 달라 이 레슨에서 측정하지 않았고, 원리(찾아서 지움 대 통째로 비움)만 설명했습니다.