大量データのテーブルを高速化・管理しやすくするOracle Partitioning。本記事ではパーティションテーブルの確認方法から、レンジ・リスト・ハッシュパーティションの作成、追加・削除・マージまで現場で使う操作を完全解説します。
目次
1. パーティションテーブルとは {#about}
パーティションとは1つのテーブルを複数の物理領域(パーティション)に分割して管理する機能です。
メリット:
- WHERE句でパーティション列を指定するとスキャン範囲が絞られて高速化
- 古いデータをパーティション単位でDROP/TRUNCATEできる(DELETE不要)
- パーティション単位でメンテナンス(バックアップ・インデックス再構築)が可能
パーティションの種類:
| 種類 | 分割基準 | 典型的な用途 |
|---|---|---|
| レンジ(RANGE) | 値の範囲(日付・数値) | 年次・月次データ |
| リスト(LIST) | 値のリスト(地域・区分) | 地域別・カテゴリ別 |
| ハッシュ(HASH) | ハッシュ値(自動均等分散) | 均等分散させたい場合 |
| コンポジット | 上記の組み合わせ | 大規模DWH |
2. パーティション情報の確認SQL {#check-partition}
パーティションテーブルの一覧を確認
-- パーティションテーブルのみを確認
SELECT TABLE_NAME, PARTITIONING_TYPE, PARTITION_COUNT, STATUS
FROM USER_PART_TABLES
ORDER BY TABLE_NAME;出力例:
TABLE_NAME PARTITIONING_TYPE PARTITION_COUNT STATUS
------------------ ----------------- --------------- ------
SALES RANGE 24 OK
CUSTOMERS LIST 5 OK
LOG_HISTORY RANGE 36 OKパーティションの詳細を確認
-- テーブルの各パーティション情報
SELECT
PARTITION_NAME,
PARTITION_POSITION,
HIGH_VALUE, -- パーティションの上限値
NUM_ROWS, -- 行数(統計情報から)
BLOCKS,
LAST_ANALYZED
FROM USER_TAB_PARTITIONS
WHERE TABLE_NAME = 'SALES'
ORDER BY PARTITION_POSITION;出力例:
PARTITION_NAME PARTITION_POSITION HIGH_VALUE NUM_ROWS LAST_ANALYZED
-------------- ------------------ ---------------------- -------- ----------------
P2024_01 1 TO_DATE('2024-02-01') 1250000 2024-02-01 02:00
P2024_02 2 TO_DATE('2024-03-01') 1180000 2024-03-01 02:00
P2026_04 27 TO_DATE('2026-05-01') 890000 2026-05-01 02:00
P_MAX 28 MAXVALUE 0 (null)パーティションインデックスの確認
-- パーティションインデックスを確認
SELECT INDEX_NAME, PARTITIONING_TYPE, LOCALITY, ALIGNMENT
FROM USER_PART_INDEXES
WHERE TABLE_NAME = 'SALES';- LOCALITY = LOCAL:テーブルと同じパーティション構成のインデックス(推奨)
- LOCALITY = GLOBAL:独立したパーティション構成のインデックス
3. レンジパーティションの作成と管理 {#range-partition}
日付や数値の範囲でパーティションを分割する最も一般的な方式です。
月次レンジパーティションの作成
CREATE TABLE SALES (
SALE_ID NUMBER,
SALE_DATE DATE NOT NULL,
CUSTOMER_ID NUMBER,
AMOUNT NUMBER(12, 2),
REGION VARCHAR2(20)
)
PARTITION BY RANGE (SALE_DATE) (
PARTITION P2024_01 VALUES LESS THAN (DATE '2024-02-01'),
PARTITION P2024_02 VALUES LESS THAN (DATE '2024-03-01'),
PARTITION P2024_03 VALUES LESS THAN (DATE '2024-04-01'),
PARTITION P2024_Q2 VALUES LESS THAN (DATE '2024-07-01'), -- 四半期でまとめることも可
PARTITION P_MAX VALUES LESS THAN (MAXVALUE) -- 上限なし(必須)
);
VALUES LESS THAN (MAXVALUE)を最後に付けないと、上限を超えるデータのINSERT時にエラーが発生します。
月次パーティションを自動作成するINTERVALパーティション(11g以降)
-- INTERVALパーティション:月が増えるたびに自動でパーティションが作られる
CREATE TABLE SALES_INTERVAL (
SALE_ID NUMBER,
SALE_DATE DATE NOT NULL,
AMOUNT NUMBER(12, 2)
)
PARTITION BY RANGE (SALE_DATE)
INTERVAL (NUMTOYMINTERVAL(1, 'MONTH')) -- 1ヶ月単位で自動作成
(
PARTITION P_INIT VALUES LESS THAN (DATE '2024-01-01') -- 最初の1つだけ必要
);INTERVALパーティションは自動で月次パーティションが追加されるため、ALTER TABLE ADD PARTITION が不要になります。
4. リストパーティションの作成と管理 {#list-partition}
特定の値のリストでパーティションを分割します。地域・区分コードなどに適しています。
CREATE TABLE CUSTOMERS (
CUSTOMER_ID NUMBER,
REGION VARCHAR2(20) NOT NULL,
NAME VARCHAR2(100),
CREATED_AT DATE
)
PARTITION BY LIST (REGION) (
PARTITION P_EAST VALUES ('東京', '神奈川', '千葉', '埼玉'),
PARTITION P_KANSAI VALUES ('大阪', '京都', '兵庫', '奈良'),
PARTITION P_OTHER VALUES (DEFAULT) -- 上記以外を全て格納
);
VALUES (DEFAULT)を付けることで未定義の値が来ても格納できます(Oracleの推奨)。
5. ハッシュパーティションの作成 {#hash-partition}
指定列のハッシュ値でデータを均等に分散させます。分割基準が特になく、I/O負荷を分散させたい場合に使います。
CREATE TABLE LOG_DATA (
LOG_ID NUMBER,
USER_ID NUMBER NOT NULL,
LOG_MSG VARCHAR2(4000),
LOG_TIME DATE
)
PARTITION BY HASH (USER_ID)
PARTITIONS 8 -- 8つのパーティションに均等分散
STORE IN (TBS1, TBS2, TBS3, TBS4, TBS5, TBS6, TBS7, TBS8); -- 各テーブルスペースを指定6. パーティションの追加・削除・マージ・分割 {#manage-partition}
パーティションの追加(ADD PARTITION)
-- レンジパーティションに新しい月を追加(MAXVALUE の前に追加が必要)
-- MAXVALUE がある場合は SPLIT PARTITION で分割する方法を使う
-- MAXVALUE がない場合の追加
ALTER TABLE SALES ADD PARTITION P2026_05
VALUES LESS THAN (DATE '2026-06-01');
-- リストパーティションに値を追加
ALTER TABLE CUSTOMERS
ADD PARTITION P_HOKKAIDO VALUES ('北海道', '青森', '秋田');MAXVALUE パーティションがある場合の月次追加(SPLIT)
-- P_MAX を分割して新しい月パーティションを作成
ALTER TABLE SALES SPLIT PARTITION P_MAX
AT (DATE '2026-06-01')
INTO (
PARTITION P2026_05,
PARTITION P_MAX
);パーティションのTRUNCATE(古いデータを高速削除)
-- 特定パーティションのデータを高速削除(テーブル構造は残る)
ALTER TABLE SALES TRUNCATE PARTITION P2024_01;DELETEより圧倒的に高速。UNDOも生成しないため大量データの削除に最適。
パーティションのDROP(パーティションごと削除)
-- パーティションとそのデータを完全に削除
ALTER TABLE SALES DROP PARTITION P2024_01;
-- INTERVALパーティションの場合
ALTER TABLE SALES DROP PARTITION FOR (DATE '2024-01-15');パーティションのMERGE(2つを1つに統合)
-- 隣接する2つのパーティションを1つにまとめる
ALTER TABLE SALES MERGE PARTITIONS P2024_01, P2024_02
INTO PARTITION P2024_Q1;7. パーティションプルーニング(クエリの高速化) {#pruning}
パーティション列をWHERE句に指定することで、該当パーティションだけをスキャンする「パーティションプルーニング」が効きます。
実行計画でパーティションプルーニングを確認
EXPLAIN PLAN FOR
SELECT * FROM SALES
WHERE SALE_DATE BETWEEN DATE '2026-04-01' AND DATE '2026-04-30';
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);プルーニングが効いている場合の出力:
-----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Pstart | Pstop |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | |
| 1 | PARTITION RANGE SINGLE | | | 27 | 27 | ← 特定パーティションのみ
| 2 | TABLE ACCESS FULL | SALES | | 27 | 27 |
------------------------------------------------------------------------------------ Pstart と Pstop が同じ番号:特定パーティションのみスキャン
- Pstart=1, Pstop=28:全パーティションスキャン(プルーニング失敗)
プルーニングが効かない典型的なパターン:
-- NG:パーティション列に関数を使う
SELECT * FROM SALES WHERE TRUNC(SALE_DATE, 'MM') = DATE '2026-04-01';
-- OK:パーティション列をそのまま比較
SELECT * FROM SALES WHERE SALE_DATE >= DATE '2026-04-01'
AND SALE_DATE < DATE '2026-05-01';8. パーティションのメンテナンス操作 {#maintenance}
パーティション単位で統計情報を収集
-- 特定パーティションの統計情報を更新
EXEC DBMS_STATS.GATHER_TABLE_STATS(
ownname => 'SCOTT',
tabname => 'SALES',
partname => 'P2026_04',
granularity => 'PARTITION'
);パーティション単位でインデックスを再構築
-- パーティションのインデックスを再構築
ALTER INDEX IDX_SALES_DATE REBUILD PARTITION P2026_04;グローバルインデックスの再構築(DROPやTRUNCATEの後)
-- パーティションのDROP/TRUNCATE後にグローバルインデックスが無効になる場合
ALTER TABLE SALES DROP PARTITION P2024_01
UPDATE GLOBAL INDEXES; -- これを付けるとグローバルインデックスを自動維持(処理は重い)
-- または事後にREBUILD
ALTER INDEX IDX_SALES_GLOBAL REBUILD;9. まとめ:パーティション管理チェックリスト {#summary}
設計時
□ パーティション列は NULL を含まない列を選ぶ
□ レンジパーティションは必ず MAXVALUE(または VALUES (DEFAULT))を末尾に付ける
□ 月次追加が必要ならINTERVALパーティションで自動化を検討
□ グローバルインデックスより LOCAL インデックスを優先
運用時
□ 月次バッチ:新しい月のパーティションを事前に ADD または SPLIT で追加
□ 古いデータは DELETE より TRUNCATE PARTITION / DROP PARTITION を使う
□ DROP/TRUNCATE後はグローバルインデックスの状態を確認・再構築
□ 月次統計情報収集はパーティション単位で実施して効率化
クエリ確認
□ 実行計画で Pstart/Pstop を確認してプルーニングが効いているか確認
□ WHERE句のパーティション列に関数・型変換を使わない関連記事:









コメント