テーブルの行数が数千万〜数億行に達すると、フルスキャンやインデックスアクセスだけでは限界が来ます。Oracleのパーティションテーブルは、1つの論理テーブルを複数の物理セグメント(パーティション)に分割して格納する仕組みで、大規模データの検索性能・メンテナンス効率・可用性を大幅に向上させます。本記事では主要なパーティション方式の作成方法から、運用で必要なパーティション操作まで体系的に解説します。
- パーティションテーブルのメリットと使いどころ
- RANGE パーティション(日付・数値の範囲で分割)の作成方法
- LIST パーティション(値リストで分割)の作成方法
- HASH パーティション(ハッシュで均等分散)の作成方法
- INTERVAL パーティション(自動作成)の作成方法
- コンポジットパーティション(RANGE-LIST 等の2段分割)
- パーティションの追加・削除・TRUNCATE・結合・交換の操作
- パーティション情報の確認SQL
- パーティションプルーニングの仕組み
パーティションテーブルのメリット
| メリット | 説明 |
|---|---|
| 検索性能の向上 | WHERE句の条件に合致するパーティションだけをスキャンする(パーティションプルーニング) |
| メンテナンス効率 | 古いデータのパーティションだけを DROP/TRUNCATE でき、DELETE より圧倒的に高速 |
| 並列処理の効率化 | パーティション単位で並列スキャン・並列DMLが可能 |
| バックアップの柔軟性 | パーティション単位で表領域を分けてバックアップ・リストアできる |
| 可用性の向上 | 1つのパーティションが破損しても他のパーティションは参照可能 |
RANGE パーティション ── 範囲で分割する
最も一般的なパーティション方式です。日付や数値の範囲を基準にデータを分割します。ログテーブルや売上テーブルなど、日付ベースのデータに最適です。
CREATE TABLE sales (
sale_id NUMBER,
sale_date DATE,
amount NUMBER,
customer_id NUMBER
)
PARTITION BY RANGE (sale_date) (
PARTITION p_2025_01 VALUES LESS THAN (TO_DATE('2025-02-01', 'YYYY-MM-DD')),
PARTITION p_2025_02 VALUES LESS THAN (TO_DATE('2025-03-01', 'YYYY-MM-DD')),
PARTITION p_2025_03 VALUES LESS THAN (TO_DATE('2025-04-01', 'YYYY-MM-DD')),
PARTITION p_2025_04 VALUES LESS THAN (TO_DATE('2025-05-01', 'YYYY-MM-DD')),
PARTITION p_max VALUES LESS THAN (MAXVALUE) -- どの範囲にも入らないデータ用
);
VALUES LESS THAN (MAXVALUE) を定義しておくと、想定外の日付のデータもエラーにならず格納されます。ただしMAXVALUEパーティションがあると、新しいパーティションの追加時に SPLIT が必要になるため、後述の INTERVAL パーティションの利用も検討してください。
CREATE TABLE order_archive (
order_id NUMBER,
order_date DATE,
status VARCHAR2(20)
)
PARTITION BY RANGE (order_id) (
PARTITION p_0_1m VALUES LESS THAN (1000000),
PARTITION p_1m_2m VALUES LESS THAN (2000000),
PARTITION p_2m_3m VALUES LESS THAN (3000000),
PARTITION p_max VALUES LESS THAN (MAXVALUE)
);
LIST パーティション ── 値のリストで分割する
特定の値(地域コード・ステータス・カテゴリ等)でデータを分割します。値の種類が少なく明確に決まっている場合に適しています。
CREATE TABLE customers (
customer_id NUMBER,
name VARCHAR2(100),
region VARCHAR2(20)
)
PARTITION BY LIST (region) (
PARTITION p_kanto VALUES ('東京', '神奈川', '千葉', '埼玉'),
PARTITION p_kansai VALUES ('大阪', '京都', '兵庫'),
PARTITION p_other VALUES (DEFAULT) -- 上記以外の値を受け入れるデフォルトパーティション
);
HASH パーティション ── ハッシュ値で均等分散する
パーティションキーのハッシュ値でデータを均等に分散します。明確な範囲やリストがない場合、または特定のパーティションにデータが偏るのを防ぎたい場合に使います。
CREATE TABLE access_log (
log_id NUMBER,
user_id NUMBER,
access_time TIMESTAMP,
url VARCHAR2(500)
)
PARTITION BY HASH (user_id)
PARTITIONS 4; -- 4つのパーティションに均等分散
-- パーティション名は SYS_P#### のように自動生成される
CREATE TABLE access_log (
log_id NUMBER,
user_id NUMBER,
access_time TIMESTAMP,
url VARCHAR2(500)
)
PARTITION BY HASH (user_id) (
PARTITION p_hash_1,
PARTITION p_hash_2,
PARTITION p_hash_3,
PARTITION p_hash_4
);
パーティション数を2, 4, 8, 16 のように2のべき乗にすると、ハッシュ分散が最も均等になります。3や5のような数では偏りが生じる可能性があります。
INTERVAL パーティション ── パーティションを自動作成する
RANGE パーティションの拡張で、指定した間隔(月・日・数値)のパーティションがデータ挿入時に自動的に作成されます。事前にパーティションを定義する手間がなくなるため、日付ベースのテーブルで特に便利です。
CREATE TABLE event_log (
event_id NUMBER,
event_date DATE,
event_type VARCHAR2(50),
detail VARCHAR2(500)
)
PARTITION BY RANGE (event_date)
INTERVAL (NUMTOYMINTERVAL(1, 'MONTH')) -- 1ヶ月間隔で自動作成
(
PARTITION p_initial VALUES LESS THAN (TO_DATE('2025-01-01', 'YYYY-MM-DD'))
);
-- 2025-03-15 のデータをINSERTすると、2025-03 用のパーティションが自動生成される
CREATE TABLE daily_metrics (
metric_date DATE,
metric_name VARCHAR2(100),
value NUMBER
)
PARTITION BY RANGE (metric_date)
INTERVAL (NUMTODSINTERVAL(1, 'DAY')) -- 1日間隔で自動作成
(
PARTITION p_initial VALUES LESS THAN (TO_DATE('2025-01-01', 'YYYY-MM-DD'))
);
コンポジットパーティション ── 2段階で分割する
1つのパーティションキーだけでは不十分な場合、2段階の分割(コンポジットパーティション)を使います。例えば「月ごとにRANGE分割 → さらに地域ごとにLIST分割」といった構成が可能です。
CREATE TABLE sales_detail (
sale_id NUMBER,
sale_date DATE,
region VARCHAR2(20),
amount NUMBER
)
PARTITION BY RANGE (sale_date)
SUBPARTITION BY LIST (region)
SUBPARTITION TEMPLATE (
SUBPARTITION sp_east VALUES ('東京', '神奈川'),
SUBPARTITION sp_west VALUES ('大阪', '京都'),
SUBPARTITION sp_other VALUES (DEFAULT)
)
(
PARTITION p_2025_q1 VALUES LESS THAN (TO_DATE('2025-04-01', 'YYYY-MM-DD')),
PARTITION p_2025_q2 VALUES LESS THAN (TO_DATE('2025-07-01', 'YYYY-MM-DD')),
PARTITION p_2025_q3 VALUES LESS THAN (TO_DATE('2025-10-01', 'YYYY-MM-DD')),
PARTITION p_2025_q4 VALUES LESS THAN (TO_DATE('2026-01-01', 'YYYY-MM-DD'))
);
-- 各パーティション内に sp_east / sp_west / sp_other の3サブパーティションが作られる
| コンポジットの組み合わせ | 用途例 |
|---|---|
| RANGE – LIST | 月ごと × 地域ごと |
| RANGE – HASH | 月ごと × ユーザーIDで均等分散 |
| LIST – HASH | カテゴリごと × IDで均等分散 |
| LIST – LIST | 地域ごと × ステータスごと |
パーティションの操作(追加・削除・TRUNCATE・結合・交換)
パーティションの追加
-- RANGE パーティションに新しい月を追加
ALTER TABLE sales
ADD PARTITION p_2025_05 VALUES LESS THAN (TO_DATE('2025-06-01', 'YYYY-MM-DD'));
-- LIST パーティションに新しい値のリストを追加
ALTER TABLE customers
ADD PARTITION p_tohoku VALUES ('宮城', '岩手', '秋田');
パーティションの削除
-- パーティションとデータを一括削除(DELETE より圧倒的に高速) ALTER TABLE sales DROP PARTITION p_2025_01; -- グローバルインデックスを保持したまま削除する場合 ALTER TABLE sales DROP PARTITION p_2025_01 UPDATE INDEXES;
パーティションの TRUNCATE
-- パーティション構造を残したままデータだけ即座に削除 ALTER TABLE sales TRUNCATE PARTITION p_2025_01; -- グローバルインデックスを保持する場合 ALTER TABLE sales TRUNCATE PARTITION p_2025_01 UPDATE INDEXES;
パーティションの分割(SPLIT)
-- MAXVALUE パーティションを分割して新しい月を追加
ALTER TABLE sales SPLIT PARTITION p_max
AT (TO_DATE('2025-06-01', 'YYYY-MM-DD'))
INTO (
PARTITION p_2025_05,
PARTITION p_max
);
パーティションの結合(MERGE)
-- 隣接する2つのパーティションを結合
ALTER TABLE sales MERGE PARTITIONS p_2025_01, p_2025_02
INTO PARTITION p_2025_q1;
パーティション交換(EXCHANGE)
-- 事前にロード済みのステージングテーブルとパーティションを瞬時に入れ替え
ALTER TABLE sales
EXCHANGE PARTITION p_2025_03
WITH TABLE sales_staging_2025_03
INCLUDING INDEXES
WITHOUT VALIDATION;
-- テーブルのデータとパーティションのデータがメタデータレベルで入れ替わる
-- 大量データのロードを無停止で行うテクニック
ステージングテーブルにデータをロードし、検証後にパーティション交換するパターンは、大規模データの「無停止切り替え」で広く使われるテクニックです。交換自体はメタデータの書き換えのみなので瞬時に完了します。
パーティション情報の確認SQL
-- テーブルのパーティション一覧
SELECT
partition_name,
high_value, -- パーティションの上限値(RANGE/LISTの場合)
partition_position, -- パーティションの順番
num_rows, -- 行数(統計収集後に反映)
blocks, -- 使用ブロック数
last_analyzed -- 最終統計収集日時
FROM user_tab_partitions
WHERE table_name = 'SALES'
ORDER BY partition_position;
SELECT
partition_name,
subpartition_name,
subpartition_position,
num_rows,
last_analyzed
FROM user_tab_subpartitions
WHERE table_name = 'SALES_DETAIL'
ORDER BY partition_name, subpartition_position;
SELECT
table_name,
partitioned -- YES=パーティションテーブル, NO=通常テーブル
FROM user_tables
WHERE table_name = 'SALES';
パーティションプルーニングの仕組み
パーティションプルーニングは、WHERE句の条件に基づいて不要なパーティションを自動的にスキップする最適化です。パーティションテーブルの性能メリットの大部分はこの仕組みに依存しています。
-- sale_date が 2025-03 に限定されるため、p_2025_03 パーティションだけスキャンされる
SELECT * FROM sales
WHERE sale_date BETWEEN TO_DATE('2025-03-01', 'YYYY-MM-DD')
AND TO_DATE('2025-03-31', 'YYYY-MM-DD');
-- 実行計画で確認する
EXPLAIN PLAN FOR
SELECT * FROM sales
WHERE sale_date >= TO_DATE('2025-03-01', 'YYYY-MM-DD')
AND sale_date < TO_DATE('2025-04-01', 'YYYY-MM-DD');
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
-- 「PARTITION RANGE SINGLE」や「Pstart=3 Pstop=3」が表示されればプルーニングが効いている
- パーティションキー列に関数を適用している(例:
WHERE TRUNC(sale_date) = ...) - パーティションキー列で暗黙の型変換が発生している
- WHERE句にパーティションキー列の条件がない
実行計画の Pstart/Pstop 列でプルーニング範囲を確認し、想定どおりか検証してください。実行計画の読み方は「【Oracle】SQLの実行計画を確認する方法」を参照してください。
パーティション方式の選び方
| 方式 | 分割基準 | 適したデータ | 自動パーティション作成 |
|---|---|---|---|
| RANGE | 値の範囲(日付・数値) | ログ・売上・履歴データ | 不可(手動追加) |
| INTERVAL | RANGEの自動版 | 日付ベースのデータ(月次・日次) | 可能(INSERT時に自動作成) |
| LIST | 値のリスト | 地域・カテゴリ・ステータス | 不可(手動追加) |
| HASH | ハッシュ値 | 偏りなく分散したいデータ | 不可(作成時に固定) |
まとめ
パーティションテーブルは大規模データのパフォーマンスとメンテナンス効率を劇的に改善する、Oracleの重要機能です。
- RANGE:日付・数値の範囲で分割する最も一般的な方式。ログや売上データに最適
- LIST:地域コードやカテゴリなど、離散的な値で分割する方式
- HASH:ハッシュで均等分散する方式。パーティション数は2のべき乗が推奨
- INTERVAL:RANGEの拡張で、INSERT時にパーティションが自動作成される。日付ベースのテーブルに最適
- コンポジット:RANGE-LIST やRANGE-HASH等の2段階分割で、より細かい管理が可能
- パーティション操作:ADD / DROP / TRUNCATE / SPLIT / MERGE / EXCHANGE で柔軟に管理。EXCHANGE PARTITIONは大量データの無停止切り替えに活用
- パーティションプルーニング:WHERE句にパーティションキーの条件があれば自動的に不要なパーティションがスキップされる。キー列への関数適用は避ける

