パーティションテーブルのインデックスは、テーブルのパーティション構成に合わせたローカルインデックスと、独自のパーティション構成または非パーティションのグローバルインデックスに分かれます。
どちらを選ぶかは、パーティション刈り込みの効果・パーティション操作(TRUNCATE / DROP)後のメンテナンスコスト・一意制約の実装要件によって決まります。選択を誤るとパーティション操作のたびにインデックス再構築が必要になるなど、運用コストに大きく影響します。
この記事でわかること
- ローカルパーティションインデックス(LOCAL)の仕組みと作成方法
- グローバルパーティションインデックス(GLOBAL)との違いと使い分け
- パーティション操作後のインデックス UNUSABLE 問題と UPDATE GLOBAL INDEXES
- PREFIXED / NONPREFIXED インデックスの違い
- USER_IND_PARTITIONS でインデックスパーティションの状態を確認する方法
パーティションインデックスの種類
| 種類 | 特徴 | 主な用途 |
|---|---|---|
| ローカル(LOCAL) | テーブルと同じパーティション構成・パーティション境界も一致する | パーティション刈り込み・パーティション操作が頻繁な場合(メンテナンスが容易) |
| グローバルパーティション(GLOBAL PARTITION) | テーブルとは異なるパーティション構成を持つ | パーティションキー以外のキーで範囲検索が多い場合 |
| グローバル非パーティション(GLOBAL 非分割) | パーティションなし(通常のインデックス)。テーブル全パーティションに跨る | 一意インデックス(UNIQUE)・テーブル全体に跨る等価検索 |
ローカルパーティションインデックス(LOCAL)
LOCAL を指定すると、テーブルのパーティションと1対1で対応するインデックスが作成されます。テーブルのパーティション境界が変更されると、インデックスも自動的に追随します。
ローカルインデックスの作成と確認
-- パーティションテーブル(例: 月別範囲パーティション)
CREATE TABLE sales_history (
sale_id NUMBER,
sale_date DATE,
customer_id NUMBER,
amount NUMBER,
region VARCHAR2(20)
)
PARTITION BY RANGE (sale_date) (
PARTITION p_2024_q1 VALUES LESS THAN (DATE '2024-04-01'),
PARTITION p_2024_q2 VALUES LESS THAN (DATE '2024-07-01'),
PARTITION p_2024_q3 VALUES LESS THAN (DATE '2024-10-01'),
PARTITION p_2024_q4 VALUES LESS THAN (DATE '2025-01-01'),
PARTITION p_max VALUES LESS THAN (MAXVALUE)
);
-- ローカルインデックスの作成(LOCAL を付けるだけ)
-- PREFIXED: インデックスキーの先頭がパーティションキーを含む(sale_date が先頭)
CREATE INDEX sales_hist_date_idx ON sales_history (sale_date, customer_id) LOCAL;
-- NONPREFIXED: パーティションキーを先頭に含まない(customer_id のみ)
CREATE INDEX sales_hist_cust_idx ON sales_history (customer_id) LOCAL;
-- LOCAL インデックスはパーティションと同じ境界・同じ数のインデックスパーティションを持つ
-- パーティションの追加・削除でインデックスも自動的に追加・削除される
-- インデックスのパーティション状態を確認する
SELECT
index_name,
partition_name,
status, -- USABLE / UNUSABLE
num_rows,
last_analyzed
FROM USER_IND_PARTITIONS
WHERE index_name = 'SALES_HIST_DATE_IDX'
ORDER BY partition_position;
PREFIXED vs NONPREFIXED ローカルインデックス
- PREFIXED:インデックスキーの先頭列がパーティションキーと一致する。パーティション刈り込みが効き、クエリ効率が高い
- NONPREFIXED:パーティションキーを含まない。WHERE 句にパーティションキーがない場合でも使えるが、全パーティションを走査する可能性がある
- 一意制約(UNIQUE)をローカルインデックスで実装する場合、パーティションキーを一意インデックスキーに含める必要がある(ローカル一意 = パーティションキーを含む一意性)
グローバルパーティションインデックス(GLOBAL)
グローバルインデックスの作成とデメリット
-- グローバル非パーティションインデックス(テーブル全体に跨る通常のインデックス)
-- UNIQUE 制約はグローバルインデックスで実装する(テーブル全体での一意性が保証できるため)
CREATE UNIQUE INDEX sales_hist_id_uk ON sales_history (sale_id); -- UNIQUE: グローバルのみ可
-- グローバルパーティションインデックス: 独自のパーティション境界を指定できる
-- 例: テーブルは sale_date でパーティションされているが、インデックスは customer_id で分割
CREATE INDEX sales_hist_gcust_idx ON sales_history (customer_id)
GLOBAL PARTITION BY RANGE (customer_id) (
PARTITION p_cust_1 VALUES LESS THAN (10000),
PARTITION p_cust_2 VALUES LESS THAN (20000),
PARTITION p_cust_max VALUES LESS THAN (MAXVALUE)
);
-- グローバルインデックスのデメリット:
-- テーブルのパーティション操作(TRUNCATE / DROP PARTITION)後に UNUSABLE になる
-- 例: p_2024_q1 パーティションを TRUNCATE するとグローバルインデックスが UNUSABLE になる
ALTER TABLE sales_history TRUNCATE PARTITION p_2024_q1;
-- → グローバルインデックス sales_hist_id_uk が UNUSABLE 状態になる
-- UNUSABLE になったインデックスを確認する
SELECT index_name, status FROM USER_INDEXES
WHERE table_name = 'SALES_HISTORY' AND status = 'UNUSABLE';
-- UNUSABLE インデックスを再構築する
ALTER INDEX sales_hist_id_uk REBUILD;
-- パーティションインデックスの場合
ALTER INDEX sales_hist_gcust_idx REBUILD PARTITION p_cust_1;
UPDATE GLOBAL INDEXES でパーティション操作とインデックス維持を同時に行う
UPDATE GLOBAL INDEXES でグローバルインデックスを維持する
-- UPDATE GLOBAL INDEXES: パーティション操作と同時にグローバルインデックスを更新する
-- → インデックスが UNUSABLE にならないため、操作後のインデックス再構築が不要
ALTER TABLE sales_history TRUNCATE PARTITION p_2024_q1 UPDATE GLOBAL INDEXES;
ALTER TABLE sales_history DROP PARTITION p_2024_q1 UPDATE GLOBAL INDEXES;
ALTER TABLE sales_history EXCHANGE PARTITION p_2024_q1
WITH TABLE sales_history_staging UPDATE GLOBAL INDEXES;
-- デメリット: UPDATE GLOBAL INDEXES を使うとパーティション操作自体に時間がかかる
-- → 削除するパーティションのデータ量に比例してインデックスのメンテナンスコストが発生
-- → 大量データのパーティションを DROP する場合は UPDATE GLOBAL INDEXES なしで
-- DROP → REBUILD の2ステップの方が速い場合がある
-- パーティション追加はグローバルインデックスに影響しない(UNUSABLE にならない)
ALTER TABLE sales_history ADD PARTITION p_2025_q1 VALUES LESS THAN (DATE '2025-04-01');
-- グローバルインデックスへの影響なし → REBUILD 不要
-- 新しいパーティションに LOCAL インデックスが USABLE かどうかを確認する
SELECT partition_name, status FROM USER_IND_PARTITIONS
WHERE index_name = 'SALES_HIST_DATE_IDX'
ORDER BY partition_position;
まとめ
- ローカルインデックス(LOCAL):テーブルと同じパーティション境界を持つ。パーティション操作後に UNUSABLE にならず、メンテナンスが容易。パーティション刈り込みが有効でほとんどの用途に適している
- グローバルインデックス:テーブル全体に跨る一意制約の実装にはグローバルインデックスが必要。パーティション操作後に UNUSABLE になるため UPDATE GLOBAL INDEXES か REBUILD が必要
- PREFIXED / NONPREFIXED:PREFIXED(先頭列がパーティションキー)はパーティション刈り込みが効く。NONPREFIXED はパーティションキーなしの条件でも使えるが全パーティションを走査する可能性がある
- UPDATE GLOBAL INDEXES:TRUNCATE/DROP PARTITION の際にグローバルインデックスを同時に更新してUSABLE を維持する。データ量が多い場合はパーティション操作のコストが増加する
- USER_IND_PARTITIONS:パーティションインデックスの各パーティションの USABLE/UNUSABLE 状態・統計を確認できる
パーティションテーブルの設計・パーティション操作の詳細は Oracle パーティションテーブル完全ガイドを参照してください。インデックス全般(B-Tree・ビットマップ・関数インデックスなど)については Oracle インデックス完全ガイドも参照してください。