【完全ガイド】ORA-01502: index or partition of such index is in unusable state の原因と解決方法|Direct Path Insert・パーティション・REBUILD 徹底解説
- 作成日 2026.09.02
- Oracle Database
Oracle DBA が本番運用中に遭遇するインデックス系の重要エラー:
SELECT * FROM orders WHERE order_id = 12345;
*
ERROR at line 1:
ORA-01502: index 'APP.ORDERS_PK' or partition of such index
is in unusable state
**「インデックスまたはパーティションが使用不可状態」**というシンプルなメッセージ。インデックスに依存する SQL 全停止の重大な問題です:
- PRIMARY KEY / UNIQUE 制約のインデックスが UNUSABLE → DML 全停止
- 通常のインデックスが UNUSABLE → SELECT パフォーマンス激低下
- パーティションインデックスの一部 UNUSABLE → 該当パーティションのみ問題
- DBA 緊急対応が必要
- アプリ全体への影響
このエラーの本質は、Oracle がインデックスを「マーク UNUSABLE」状態と判定して使用拒否:
インデックスの STATUS:
VALID: 通常
UNUSABLE: 使用不可(本記事)
N/A: パーティション・インデックス本体
UNUSABLE になる契機:
- Direct Path Insert (SQL*Loader)
- ALTER TABLE MOVE
- パーティション DDL
- ALTER INDEX ... UNUSABLE 明示
- REBUILD 失敗・中断
- 領域不足
- 1. 結論:ALTER INDEX … REBUILD で復旧
- 2. Oracle インデックスの仕組み
- 3. 【原因①】Direct Path Insert(最頻出)
- 4. 【原因②】ALTER TABLE MOVE
- 5. 【原因③】パーティション DDL
- 6. 【原因④】ALTER INDEX … UNUSABLE 明示
- 7. 【原因⑤】Data Pump インポート
- 8. 【原因⑥】REBUILD 中断
- 9. 【原因⑦】一意制約違反(CREATE UNIQUE INDEX 失敗)
- 10. 【原因⑧】領域不足
- 11. 【原因⑨】IOT (Index-Organized Table) 再編成
- 12. 【原因⑩】システムクラッシュ中の DDL
- 13. 診断ツール完全リファレンス
- 14. 6つの解決策 完全リファレンス
- 15. Rails / Java / Python 対応
- 16. 実践シナリオ
- 17. トラブルシューティング
- 18. よくある質問(FAQ)
- 18.1. Q1. REBUILD と REBUILD ONLINE の違い
- 18.2. Q2. PARTITION 単位の REBUILD
- 18.3. Q3. SKIP_UNUSABLE_INDEXES の効果
- 18.4. Q4. UPDATE INDEXES clause
- 18.5. Q5. NOLOGGING のリスク
- 18.6. Q6. Global vs Local
- 18.7. Q7. Rails での対応
- 18.8. Q8. Java での対応
- 18.9. Q9. Python での対応
- 18.10. Q10. パフォーマンス影響
- 18.11. Q11. 予防策
- 18.12. Q12. Autonomous DB での挙動
- 19. 参考リンク
- 20. まとめ
インデックスの STATUS 階層
Oracle パーティションインデックスの STATUS:
【通常インデックス】
DBA_INDEXES.STATUS:
VALID / UNUSABLE
【パーティションインデックス】
DBA_INDEXES.STATUS: N/A (常に)
DBA_IND_PARTITIONS.STATUS: USABLE / UNUSABLE / N/A
【サブパーティションインデックス】
DBA_IND_SUBPARTITIONS.STATUS: USABLE / UNUSABLE
PRIMARY KEY 制約のインデックス UNUSABLE の影響
重大な影響:
PRIMARY KEY 制約 → 内部で UNIQUE INDEX 使用
↓
UNIQUE INDEX が UNUSABLE
↓
INSERT / UPDATE / DELETE 不可
↓
アプリ完全停止
CA Clarity(PPM ソフトウェア)等の企業アプリでは深刻な障害として頻繁に報告されています:
[CA Clarity][Oracle JDBC Driver]
ORA-01502: index 'NIKU.CMN_SEC_ASSGND_OBJ_PERM_PK'
or partition of such index is in unusable state
現場で最も典型的なパターン:
- Direct Path Insert(SQL*Loader
DIRECT=Y)(最頻出) ALTER TABLE MOVE後の INDEX 再構築忘れ- パーティション DDL(SPLIT/MERGE/TRUNCATE/MOVE/EXCHANGE)
ALTER INDEX ... UNUSABLE明示(メンテナンス用途)- Data Pump インポート(
impdp) REBUILD中断(領域不足・接続断)- 一意制約違反(
CREATE UNIQUE INDEX失敗) - 一時領域不足(
REBUILD中) - IOT 再編成
- System crash 中の DDL
多くの日本語記事が「REBUILD せよ」で終わりますが、実務では:
REBUILDvsREBUILD ONLINEの選択基準- PARTITION 単位の REBUILD(
REBUILD PARTITION) SKIP_UNUSABLE_INDEXESセッション設定の使い所UPDATE INDEXESclause(DDL 時の自動再構築)- Global vs Local パーティションインデックスの違い
ONLINE REBUILDの業務停止最小化PARALLELオプションによる並列化NOLOGGINGの使い所(バックアップ必須)DBA_IND_PARTITIONSvsDBA_IND_SUBPARTITIONS- Rails マイグレーションでの注意点
さらに、Oracle 公式が明示する重要な仕様:
オプティマイザが UNUSABLE インデックスを選択:
SKIP_UNUSABLE_INDEXES=TRUE (デフォルト):
→ 使わない、フルスキャン
ヒントで強制:
→ ORA-01502 発生
本記事では、ORA-01502: index or partition of such index is in unusable state の完全な原因と解決方法を、リファレンスとして実用的に整理します。10大発生パターン、REBUILD 戦略、6つの解決策、パーティション対応、Rails/Java/Python 対応、実践シナリオ、FAQまで完全網羅。この1本で ORA-01502 に冷静に的確に対処できるようになります。
結論:ALTER INDEX … REBUILD で復旧
時間がない方向けに、最速の対処を先に示します。
エラーメッセージの読み方
ORA-01502: index 'SCHEMA.INDEX_NAME' or partition of such index
is in unusable state
↑
使用不可のインデックス
= インデックスが UNUSABLE マーク済み
DBA_INDEXES.STATUS または DBA_IND_PARTITIONS.STATUS で確認
最速の診断と対処
-- STEP 1: UNUSABLE インデックス一覧
SELECT owner, index_name, status
FROM dba_indexes
WHERE status = 'UNUSABLE';
-- STEP 2: UNUSABLE パーティション
SELECT index_owner, index_name, partition_name, status
FROM dba_ind_partitions
WHERE status = 'UNUSABLE';
-- STEP 3: REBUILD 実行
ALTER INDEX schema.index_name REBUILD;
-- STEP 4: PARTITION 単位
ALTER INDEX schema.index_name REBUILD PARTITION partition_name;
-- STEP 5: ONLINE で業務中対応
ALTER INDEX schema.index_name REBUILD ONLINE;
6つの解決策
| # | 手法 | 使う場面 |
|---|---|---|
| ① | REBUILD | 基本 |
| ② | REBUILD ONLINE | 業務中 |
| ③ | REBUILD PARTITION | パーティション |
| ④ | 一括スクリプト | 大量 UNUSABLE |
| ⑤ | SKIP_UNUSABLE_INDEXES | 一時回避 |
| ⑥ | UPDATE INDEXES | DDL 時予防 |
パーティションインデックス STATUS
| ビュー | STATUS 意味 |
|---|---|
| DBA_INDEXES | VALID / UNUSABLE / N/A |
| DBA_IND_PARTITIONS | USABLE / UNUSABLE / N/A |
| DBA_IND_SUBPARTITIONS | USABLE / UNUSABLE |
詳細は以下で解説します。
Oracle インデックスの仕組み
インデックスの種類
通常インデックス(B-Tree):
CREATE INDEX idx_emp_name ON emp(name);
一意インデックス:
CREATE UNIQUE INDEX idx_emp_id ON emp(id);
パーティションインデックス:
-- Local(テーブルパーティションに追従)
CREATE INDEX idx_orders_date ON orders(order_date) LOCAL;
-- Global(独立)
CREATE INDEX idx_orders_id ON orders(id) GLOBAL
PARTITION BY RANGE(id) (
PARTITION p1 VALUES LESS THAN (10000),
PARTITION p2 VALUES LESS THAN (MAXVALUE)
);
Bitmap インデックス:
CREATE BITMAP INDEX idx_status ON emp(status);
Function-Based インデックス:
CREATE INDEX idx_upper_name ON emp(UPPER(name));
インデックスの STATUS
-- 通常
SELECT index_name, status FROM user_indexes;
-- VALID / UNUSABLE
-- パーティション
SELECT index_name, partition_name, status FROM user_ind_partitions;
-- USABLE / UNUSABLE / N/A
-- サブパーティション
SELECT index_name, subpartition_name, status FROM user_ind_subpartitions;
Global vs Local
Global(テーブルパーティションと独立):
- 1 index = 全パーティション対応
- パーティション操作で全体 UNUSABLE
- 更新頻度低いテーブル向け
Local(テーブルパーティションと 1:1):
- 各テーブルパーティションに対応 index パーティション
- パーティション操作で該当のみ影響
- 大規模テーブル向け(推奨)
【原因①】Direct Path Insert(最頻出)
シナリオ
# SQL*Loader Direct Path モード
$ sqlldr scott/tiger control=data.ctl direct=y
または SQL:
INSERT /*+ APPEND */ INTO orders SELECT * FROM staging;
結果:
Direct Path Load 完了
Records loaded: 1,000,000
Indexes in use: 0
Index maintained: INDEX X marked UNUSABLE
Direct Path は インデックスをスキップ、後で REBUILD 必要。
診断
SELECT index_name, status
FROM user_indexes
WHERE table_name = 'ORDERS' AND status = 'UNUSABLE';
解決
ALTER INDEX orders_pk REBUILD;
ALTER INDEX orders_idx_date REBUILD ONLINE;
【原因②】ALTER TABLE MOVE
シナリオ
-- テーブル移動
ALTER TABLE orders MOVE TABLESPACE users;
-- インデックス UNUSABLE に!
診断
SELECT index_name, status FROM user_indexes
WHERE table_name = 'ORDERS';
-- 複数 UNUSABLE
解決
A. 個別 REBUILD:
ALTER INDEX orders_pk REBUILD;
ALTER INDEX orders_idx1 REBUILD;
B. 一括スクリプト:
BEGIN
FOR r IN (SELECT index_name FROM user_indexes
WHERE table_name = 'ORDERS' AND status = 'UNUSABLE') LOOP
EXECUTE IMMEDIATE 'ALTER INDEX ' || r.index_name || ' REBUILD';
END LOOP;
END;
/
C. 予防:MOVE 時に自動再構築(12.2+):
ALTER TABLE orders MOVE TABLESPACE users UPDATE INDEXES;
-- インデックス自動再構築
【原因③】パーティション DDL
シナリオ
-- SPLIT PARTITION
ALTER TABLE orders SPLIT PARTITION p_2025 AT (TO_DATE('2025-07-01', 'YYYY-MM-DD'))
INTO (PARTITION p_2025_h1, PARTITION p_2025_h2);
-- Global index UNUSABLE!
-- MOVE PARTITION
ALTER TABLE orders MOVE PARTITION p_2024 TABLESPACE ts_2024;
-- TRUNCATE PARTITION
ALTER TABLE orders TRUNCATE PARTITION p_2023;
-- EXCHANGE PARTITION
ALTER TABLE orders EXCHANGE PARTITION p_2024 WITH TABLE archive_2024;
診断
-- パーティション単位
SELECT index_owner, index_name, partition_name, status
FROM dba_ind_partitions
WHERE status = 'UNUSABLE';
解決
A. パーティション単位 REBUILD:
ALTER INDEX orders_idx_date REBUILD PARTITION p_2025_h1;
B. 一括:
BEGIN
FOR r IN (SELECT index_owner, index_name, partition_name
FROM dba_ind_partitions WHERE status = 'UNUSABLE') LOOP
EXECUTE IMMEDIATE
'ALTER INDEX ' || r.index_owner || '.' || r.index_name ||
' REBUILD PARTITION ' || r.partition_name;
END LOOP;
END;
/
C. 予防:DDL 時に UPDATE INDEXES / UPDATE GLOBAL INDEXES:
ALTER TABLE orders SPLIT PARTITION p_2025 AT (TO_DATE('2025-07-01', 'YYYY-MM-DD'))
INTO (PARTITION p_2025_h1, PARTITION p_2025_h2)
UPDATE INDEXES;
-- または
ALTER TABLE orders SPLIT PARTITION ...
UPDATE GLOBAL INDEXES;
【原因④】ALTER INDEX … UNUSABLE 明示
シナリオ(意図的、大量ロード前)
-- ロード前にインデックス無効化(高速化)
ALTER INDEX orders_idx1 UNUSABLE;
-- 大量 INSERT
INSERT INTO orders SELECT * FROM staging;
-- ロード後 REBUILD
ALTER INDEX orders_idx1 REBUILD;
メリット: INSERT パフォーマンス大幅向上。 注意: REBUILD 忘れると ORA-01502。
解決
必ず REBUILD:
ALTER INDEX orders_idx1 REBUILD;
【原因⑤】Data Pump インポート
シナリオ
$ impdp scott/tiger dumpfile=data.dmp
-- インポート中にエラー発生
-- 一部インデックス UNUSABLE
解決
-- UNUSABLE 特定
SELECT owner, index_name FROM dba_indexes WHERE status = 'UNUSABLE';
-- REBUILD
BEGIN
FOR r IN (SELECT owner, index_name FROM dba_indexes
WHERE status = 'UNUSABLE') LOOP
EXECUTE IMMEDIATE
'ALTER INDEX ' || r.owner || '.' || r.index_name || ' REBUILD';
END LOOP;
END;
/
Data Pump 関連は Oracle Data Pump 使い方の記事も参照してください。
【原因⑥】REBUILD 中断
シナリオ
1. ALTER INDEX big_idx REBUILD;
2. 実行中に接続断 or セッションキル
3. インデックスが UNUSABLE のまま残る
診断
SELECT index_name, status FROM user_indexes
WHERE status = 'UNUSABLE' AND index_name = 'BIG_IDX';
解決
-- 再度 REBUILD
ALTER INDEX big_idx REBUILD;
-- 領域不足なら別 TS
ALTER INDEX big_idx REBUILD TABLESPACE new_ts;
領域関連は ORA-01654: unable to extend index の記事も参照してください。
【原因⑦】一意制約違反(CREATE UNIQUE INDEX 失敗)
シナリオ
-- 重複データがあるカラムに UNIQUE INDEX
CREATE UNIQUE INDEX idx_unique ON emp(email);
-- ORA-01452: cannot CREATE UNIQUE INDEX; duplicate keys found
-- インデックスが UNUSABLE で残る場合あり
診断
-- 重複データ確認
SELECT email, COUNT(*) FROM emp GROUP BY email HAVING COUNT(*) > 1;
解決
-- 重複解消後 REBUILD
UPDATE emp SET email = email || ROWNUM WHERE ROWNUM = 1 AND email = 'dup@example.com';
-- REBUILD
ALTER INDEX idx_unique REBUILD;
【原因⑧】領域不足
シナリオ
ALTER INDEX big_idx REBUILD;
-- ORA-01654: unable to extend index by 128 in tablespace INDEXES
-- インデックスが UNUSABLE で残る
解決
A. 別 TS で REBUILD:
ALTER INDEX big_idx REBUILD TABLESPACE new_index_ts;
B. TS 拡張:
ALTER TABLESPACE indexes ADD DATAFILE '/u01/oradata/indexes_02.dbf' SIZE 5G;
ALTER INDEX big_idx REBUILD;
【原因⑨】IOT (Index-Organized Table) 再編成
シナリオ
-- IOT MOVE
ALTER TABLE iot_table MOVE;
-- 関連 index UNUSABLE
解決
ALTER TABLE iot_table MOVE UPDATE INDEXES;
【原因⑩】システムクラッシュ中の DDL
シナリオ
1. ALTER INDEX ... REBUILD 実行中
2. サーバー電源断
3. インデックス UNUSABLE のまま
解決
再度 REBUILD:
ALTER INDEX big_idx REBUILD;
Instance Recovery 後の状態確認:
SELECT index_name, status FROM user_indexes WHERE status = 'UNUSABLE';
DB 起動関連は ORA-01102: cannot mount database の記事も参照してください。
診断ツール完全リファレンス
DBA_INDEXES
-- 全 UNUSABLE インデックス
SELECT owner, index_name, table_name, status, tablespace_name
FROM dba_indexes
WHERE status = 'UNUSABLE'
ORDER BY owner, table_name, index_name;
DBA_IND_PARTITIONS
-- UNUSABLE パーティション
SELECT index_owner, index_name, partition_name, status, tablespace_name
FROM dba_ind_partitions
WHERE status = 'UNUSABLE'
ORDER BY index_owner, index_name, partition_name;
DBA_IND_SUBPARTITIONS
SELECT index_owner, index_name, partition_name, subpartition_name, status
FROM dba_ind_subpartitions
WHERE status = 'UNUSABLE';
USER_INDEXES(自スキーマ)
SELECT index_name, table_name, status
FROM user_indexes
WHERE status = 'UNUSABLE';
REBUILD 用スクリプト生成
-- 通常インデックス
SELECT 'ALTER INDEX ' || owner || '.' || index_name || ' REBUILD ONLINE;' AS cmd
FROM dba_indexes
WHERE status = 'UNUSABLE';
-- パーティション
SELECT 'ALTER INDEX ' || index_owner || '.' || index_name ||
' REBUILD PARTITION ' || partition_name || ' ONLINE;' AS cmd
FROM dba_ind_partitions
WHERE status = 'UNUSABLE';
6つの解決策 完全リファレンス
解決策① ALTER INDEX … REBUILD
-- 基本
ALTER INDEX schema.index_name REBUILD;
-- 別テーブルスペースへ
ALTER INDEX schema.index_name REBUILD TABLESPACE new_ts;
-- 並列(大規模テーブル)
ALTER INDEX schema.index_name REBUILD PARALLEL 4;
-- NOLOGGING(バックアップ必須)
ALTER INDEX schema.index_name REBUILD NOLOGGING;
解決策② ALTER INDEX … REBUILD ONLINE
-- ONLINE = 業務中に実行可
ALTER INDEX schema.index_name REBUILD ONLINE;
-- 並列 + ONLINE
ALTER INDEX schema.index_name REBUILD ONLINE PARALLEL 4;
メリット: 業務停止不要。 注意: 若干の負荷 + 一時領域増加。
解決策③ REBUILD PARTITION
-- パーティション単位
ALTER INDEX orders_idx REBUILD PARTITION p_2025;
-- ONLINE
ALTER INDEX orders_idx REBUILD PARTITION p_2025 ONLINE;
-- サブパーティション
ALTER INDEX orders_idx REBUILD SUBPARTITION sp_2025_q1;
解決策④ 一括スクリプト
-- 全 UNUSABLE インデックス REBUILD
BEGIN
FOR r IN (SELECT owner, index_name FROM dba_indexes
WHERE status = 'UNUSABLE'
AND owner NOT IN ('SYS', 'SYSTEM')) LOOP
BEGIN
EXECUTE IMMEDIATE
'ALTER INDEX ' || r.owner || '.' || r.index_name || ' REBUILD ONLINE';
DBMS_OUTPUT.PUT_LINE('OK: ' || r.owner || '.' || r.index_name);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('FAIL: ' || r.owner || '.' || r.index_name ||
' - ' || SQLERRM);
END;
END LOOP;
END;
/
-- パーティション版
BEGIN
FOR r IN (SELECT index_owner, index_name, partition_name
FROM dba_ind_partitions
WHERE status = 'UNUSABLE'
AND index_owner NOT IN ('SYS', 'SYSTEM')) LOOP
BEGIN
EXECUTE IMMEDIATE
'ALTER INDEX ' || r.index_owner || '.' || r.index_name ||
' REBUILD PARTITION ' || r.partition_name || ' ONLINE';
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('FAIL: ' || r.index_name || '.' || r.partition_name);
END;
END LOOP;
END;
/
解決策⑤ SKIP_UNUSABLE_INDEXES セッション設定
Oracle 10g+ でデフォルト TRUE、一時回避:
-- セッションレベル
ALTER SESSION SET SKIP_UNUSABLE_INDEXES = TRUE;
-- システムレベル
ALTER SYSTEM SET SKIP_UNUSABLE_INDEXES = TRUE;
-- 効果:
-- UNUSABLE インデックスをオプティマイザが選択しない
-- フルスキャンで代替(低速)
-- PRIMARY KEY/UNIQUE のみは違反時 ORA-01502 発生
⚠️ 根本解決ではない、REBUILD 必須。
解決策⑥ UPDATE INDEXES clause(予防)
DDL 時にインデックス自動再構築:
-- ALTER TABLE MOVE + 自動再構築
ALTER TABLE orders MOVE TABLESPACE new_ts UPDATE INDEXES;
-- パーティション DDL + 自動再構築
ALTER TABLE orders SPLIT PARTITION p_2025 AT (...)
INTO (PARTITION p1, PARTITION p2)
UPDATE INDEXES;
-- UPDATE GLOBAL INDEXES(グローバルのみ)
ALTER TABLE orders TRUNCATE PARTITION p_2023 UPDATE GLOBAL INDEXES;
Rails / Java / Python 対応
Rails ActiveRecord
エラーハンドリング:
begin
Order.find(order_id)
rescue ActiveRecord::StatementInvalid => e
if e.message.include?("ORA-01502")
Rails.logger.fatal "Index unusable: #{e.message}"
NotifyOps.critical("DBA action required: rebuild index")
end
end
マイグレーション後の検証:
class MoveOrdersTable < ActiveRecord::Migration[8.0]
def up
execute "ALTER TABLE orders MOVE TABLESPACE users UPDATE INDEXES"
# インデックス状態確認
unusable = execute(<<-SQL).to_a
SELECT index_name FROM user_indexes
WHERE table_name = 'ORDERS' AND status = 'UNUSABLE'
SQL
if unusable.any?
raise "Unusable indexes: #{unusable.inspect}"
end
end
end
Rails 8 系の詳細は Rails 8 アップグレードガイドの記事、rails db:migrate 使い方の記事も参照してください。
Java (JDBC)
try {
ResultSet rs = ps.executeQuery();
} catch (SQLException e) {
if (e.getErrorCode() == 1502) {
logger.fatal("Index unusable: " + e.getMessage());
// DBA 通知 + フルスキャンにフォールバック検討
}
}
Python (oracledb)
import oracledb
import logging
try:
cursor.execute("SELECT * FROM orders WHERE order_id = :1", [12345])
except oracledb.DatabaseError as e:
error_obj, = e.args
if error_obj.code == 1502:
logging.critical(f"Index unusable: {error_obj.message}")
# 自動 REBUILD スクリプト起動 or 通知
実践シナリオ
シナリオ1:本番緊急対応
-- 1. UNUSABLE 特定
SELECT owner, index_name, table_name
FROM dba_indexes WHERE status = 'UNUSABLE';
-- 2. パーティション UNUSABLE も
SELECT index_owner, index_name, partition_name
FROM dba_ind_partitions WHERE status = 'UNUSABLE';
-- 3. 一括 REBUILD ONLINE
BEGIN
FOR r IN (SELECT owner, index_name FROM dba_indexes
WHERE status = 'UNUSABLE') LOOP
EXECUTE IMMEDIATE
'ALTER INDEX ' || r.owner || '.' || r.index_name || ' REBUILD ONLINE';
END LOOP;
END;
/
-- 4. 検証
SELECT COUNT(*) FROM dba_indexes WHERE status = 'UNUSABLE';
-- 0 なら OK
シナリオ2:Direct Path Insert 後の対応
# 大量ロード
$ sqlldr scott/tiger control=big.ctl direct=y
# 完了後、インデックス確認
sqlplus scott/tiger <<EOF
SELECT index_name, status FROM user_indexes
WHERE table_name = 'BIG_TABLE' AND status = 'UNUSABLE';
EOF
# REBUILD
sqlplus scott/tiger <<EOF
BEGIN
FOR r IN (SELECT index_name FROM user_indexes
WHERE table_name = 'BIG_TABLE' AND status = 'UNUSABLE') LOOP
EXECUTE IMMEDIATE 'ALTER INDEX ' || r.index_name || ' REBUILD';
END LOOP;
END;
/
EOF
シナリオ3:パーティションメンテナンス
-- 月次パーティション追加
ALTER TABLE orders ADD PARTITION p_202606
VALUES LESS THAN (TO_DATE('2026-07-01', 'YYYY-MM-DD'));
-- 12ヶ月前のパーティション削除
ALTER TABLE orders DROP PARTITION p_202506;
-- Global index の対応
ALTER TABLE orders DROP PARTITION p_202506 UPDATE GLOBAL INDEXES;
-- または後で
ALTER INDEX orders_idx REBUILD;
シナリオ4:CA Clarity / EBS 対応
CA Clarity / Oracle EBS で ORA-01502 発生
1. DB バックアップ確認
2. アプリケーションサービス停止
3. UNUSABLE インデックス特定
4. REBUILD スクリプト実行
5. アプリケーションサービス再起動
6. 動作確認
-- 対象例:
ALTER INDEX NIKU.CMN_SEC_ASSGND_OBJ_PERM_PK REBUILD;
シナリオ5:CI/CD 統合
- name: Post-migration index check
run: |
sqlplus -s $DB_USER/$DB_PW <<EOF
WHENEVER SQLERROR EXIT SQL.SQLCODE
DECLARE
v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count FROM user_indexes WHERE status = 'UNUSABLE';
IF v_count > 0 THEN
RAISE_APPLICATION_ERROR(-20001, 'Unusable indexes: ' || v_count);
END IF;
END;
/
EOF
Kamal 2 デプロイの詳細は Kamal 2 デプロイの記事を参照してください。
シナリオ6:Docker Oracle テスト
docker exec -it oracle-xe sqlplus scott/tiger <<EOF
CREATE TABLE t (id NUMBER PRIMARY KEY, val VARCHAR2(100));
CREATE INDEX idx_val ON t(val);
-- UNUSABLE に明示
ALTER INDEX idx_val UNUSABLE;
-- 状態確認
SELECT index_name, status FROM user_indexes;
-- IDX_VAL: UNUSABLE
-- SELECT
SELECT * FROM t WHERE val = 'test';
-- 通常は SKIP_UNUSABLE_INDEXES=TRUE でフルスキャン
-- ヒント強制
SELECT /*+ INDEX(t idx_val) */ * FROM t WHERE val = 'test';
-- ORA-01502
-- REBUILD
ALTER INDEX idx_val REBUILD;
EOF
Docker 関連は docker daemon 接続エラーの記事、Docker no space left on device の記事も参照してください。
シナリオ7:大量ロード最適化
-- 1. インデックス無効化(高速化)
ALTER INDEX orders_idx1 UNUSABLE;
ALTER INDEX orders_idx2 UNUSABLE;
-- 2. 大量 INSERT(Direct Path)
INSERT /*+ APPEND */ INTO orders SELECT * FROM staging;
COMMIT;
-- 3. REBUILD ONLINE
ALTER INDEX orders_idx1 REBUILD ONLINE PARALLEL 4;
ALTER INDEX orders_idx2 REBUILD ONLINE PARALLEL 4;
-- 4. 並列度リセット
ALTER INDEX orders_idx1 NOPARALLEL;
シナリオ8:Python 監視
import oracledb
import logging
def check_unusable_indexes(dsn, user, pw):
with oracledb.connect(user=user, password=pw, dsn=dsn) as conn:
cursor = conn.cursor()
# 通常インデックス
cursor.execute("""
SELECT owner, index_name FROM dba_indexes
WHERE status = 'UNUSABLE'
AND owner NOT IN ('SYS', 'SYSTEM')
""")
unusable = cursor.fetchall()
# パーティション
cursor.execute("""
SELECT index_owner, index_name, partition_name
FROM dba_ind_partitions
WHERE status = 'UNUSABLE'
AND index_owner NOT IN ('SYS', 'SYSTEM')
""")
unusable_parts = cursor.fetchall()
if unusable or unusable_parts:
logging.critical(f"Unusable indexes: {len(unusable)} + {len(unusable_parts)} partitions")
return False
return True
シナリオ9:Rails ヘルスチェック
class DatabaseHealthController < ApplicationController
def index_check
result = ActiveRecord::Base.connection.select_all(<<-SQL).to_a
SELECT owner, index_name FROM dba_indexes
WHERE status = 'UNUSABLE'
AND owner = '#{Rails.application.config.database_owner}'
SQL
if result.any?
render json: {
status: 'CRITICAL',
unusable_indexes: result
}, status: 503
else
render json: { status: 'OK' }
end
end
end
シナリオ10:定期監視スクリプト
#!/bin/bash
# monitor_indexes.sh
count=$(sqlplus -s / as sysdba <<EOF
SET HEADING OFF FEEDBACK OFF
SELECT COUNT(*) FROM dba_indexes WHERE status = 'UNUSABLE';
EXIT;
EOF
)
if [ "$count" -gt "0" ]; then
echo "ALERT: $count unusable indexes" | mail -s "Oracle Index Alert" ops@example.com
fi
crontab の詳細は crontab 使い方の記事も参照してください。
トラブルシューティング
REBUILD が遅い
PARALLEL オプション or NOLOGGING(バックアップ必須):
ALTER INDEX big_idx REBUILD PARALLEL 8 NOLOGGING;
REBUILD で領域不足
別テーブルスペース:
ALTER INDEX big_idx REBUILD TABLESPACE new_ts;
PARTITION 数百個ある
PARALLEL 実行、並列度制限でリソース管理。
SKIP_UNUSABLE_INDEXES 効かない
PRIMARY KEY / UNIQUE 制約のみは常に効果あり、制約チェック優先。
Rails migration で発生
UPDATE INDEXES clause で予防、必ずマイグレーション後検証。
PostgreSQL からの移行
PG は REINDEX、Oracle は ALTER INDEX REBUILD。オンライン対応の違いに注意。
よくある質問(FAQ)
Q1. REBUILD と REBUILD ONLINE の違い
- REBUILD: 排他ロック、業務停止
- REBUILD ONLINE: DML 継続可、若干負荷
Q2. PARTITION 単位の REBUILD
REBUILD PARTITION、Local index の一部のみ対応可。
Q3. SKIP_UNUSABLE_INDEXES の効果
オプティマイザ回避、PRIMARY KEY/UNIQUE の DML は不可のまま。
Q4. UPDATE INDEXES clause
DDL 時に自動再構築、UNUSABLE を予防。
Q5. NOLOGGING のリスク
バックアップ必須、REDO 生成せず高速だがリカバリ不能。
Q6. Global vs Local
- Global: DDL で全体 UNUSABLE
- Local: 該当パーティションのみ
Q7. Rails での対応
マイグレーション後検証、UPDATE INDEXES 使用。
Q8. Java での対応
errorCode == 1502 検出、DBA 通知。
Q9. Python での対応
oracledb.DatabaseError.code == 1502。
Q10. パフォーマンス影響
インデックス UNUSABLE = SELECT 激低下、DML 停止も。
Q11. 予防策
- UPDATE INDEXES clause
- 大量ロード後の REBUILD 自動化
- 監視スクリプト
- CI/CD 検証
Q12. Autonomous DB での挙動
自動メンテナンスあるが、DDL 後は要確認。
参考リンク
Oracle 公式
- Oracle Database Error Messages: ORA-01502
- Oracle Database Reference: SKIP_UNUSABLE_INDEXES
- Oracle Database SQL Language Reference: ALTER INDEX
- Oracle Database Administrator’s Guide: Managing Indexes
まとめ
ORA-01502: index or partition of such index is in unusable state の要点を再整理します。
エラーの本質
インデックス or パーティションが UNUSABLE 状態
→ 使用不可、SELECT 激低下 or DML 停止
→ REBUILD で復旧
→ PRIMARY KEY/UNIQUE は特に影響大
エラーメッセージの読み方
ORA-01502: index 'SCHEMA.INDEX_NAME' or partition of such index
is in unusable state
↑
使用不可のインデックス
インデックス STATUS 階層
| ビュー | STATUS |
|---|---|
| DBA_INDEXES | VALID / UNUSABLE / N/A |
| DBA_IND_PARTITIONS | USABLE / UNUSABLE / N/A |
| DBA_IND_SUBPARTITIONS | USABLE / UNUSABLE |
10大原因
| # | 原因 | 対処 |
|---|---|---|
| ① | Direct Path Insert | REBUILD |
| ② | ALTER TABLE MOVE | UPDATE INDEXES |
| ③ | パーティション DDL | REBUILD PARTITION |
| ④ | UNUSABLE 明示 | REBUILD |
| ⑤ | Data Pump | 一括 REBUILD |
| ⑥ | REBUILD 中断 | 再度 REBUILD |
| ⑦ | 一意制約違反 | 重複解消 + REBUILD |
| ⑧ | 領域不足 | 別 TS で REBUILD |
| ⑨ | IOT 再編成 | UPDATE INDEXES |
| ⑩ | Sysem crash | 再度 REBUILD |
6つの解決策
-- ① 基本 REBUILD
ALTER INDEX idx_name REBUILD;
-- ② ONLINE(業務中)
ALTER INDEX idx_name REBUILD ONLINE;
-- ③ PARTITION 単位
ALTER INDEX idx_name REBUILD PARTITION p_202506;
-- ④ 一括スクリプト
BEGIN
FOR r IN (SELECT owner, index_name FROM dba_indexes
WHERE status = 'UNUSABLE') LOOP
EXECUTE IMMEDIATE
'ALTER INDEX ' || r.owner || '.' || r.index_name || ' REBUILD ONLINE';
END LOOP;
END;
-- ⑤ SKIP_UNUSABLE_INDEXES(一時回避)
ALTER SESSION SET SKIP_UNUSABLE_INDEXES = TRUE;
-- ⑥ UPDATE INDEXES(予防)
ALTER TABLE orders MOVE TABLESPACE new_ts UPDATE INDEXES;
診断クエリ Top 3
-- ① 通常インデックス
SELECT owner, index_name FROM dba_indexes WHERE status = 'UNUSABLE';
-- ② パーティション
SELECT index_owner, index_name, partition_name
FROM dba_ind_partitions WHERE status = 'UNUSABLE';
-- ③ REBUILD スクリプト生成
SELECT 'ALTER INDEX ' || owner || '.' || index_name || ' REBUILD ONLINE;'
FROM dba_indexes WHERE status = 'UNUSABLE';
予防のポイント
1. ALTER TABLE MOVE には UPDATE INDEXES
2. パーティション DDL には UPDATE GLOBAL INDEXES
3. Direct Path Insert 後は必ず REBUILD
4. 大量ロード時は UNUSABLE + REBUILD パターン
5. REBUILD ONLINE で業務停止最小化
6. 監視スクリプト設置
7. CI/CD で post-migration 検証
8. Rails マイグレーション後の検証
9. 定期メンテナンス(月次)
10. Local index 推奨(Global より影響小)
これらの知識は、Oracle DBA の本番運用・パフォーマンスチューニング・パーティション管理・データ移行・Rails / Java / Python アプリ運用・CI/CD パイプライン・大量データロード・CA Clarity / Oracle EBS 等の商用アプリ管理など、あらゆる場面で活用できます。本記事をブックマークしておけば、ORA-01502 に出会っても冷静に的確に対処できるようになります。
本記事は2026年6月時点の情報をもとに、Oracle Database 19c〜23ai での動作確認・公式ドキュメントに基づき作成しています。Oracle のバージョンにより挙動が異なる場合があるため、最新の情報は Oracle 公式ドキュメント(docs.oracle.com)もあわせてご確認ください。
-
前の記事
【完全ガイド】ORA-01033: ORACLE initialization or shutdown in progress の原因と解決方法|PDB MOUNTED・STARTUP 3段階 徹底解説 2026.09.01
-
次の記事
【完全ガイド】ORA-08102: index key not found の原因と解決方法|インデックス破損・REBUILD の罠 徹底解説 2026.09.03
コメントを書く