ORA-01502 Index or Partition Unusable - Complete Resolution Guide
ORA-01502: Index or Partition of Such Index is in Unusable State
Section titled “ORA-01502: Index or Partition of Such Index is in Unusable State”Error Overview
Section titled “Error Overview”Error Text: ORA-01502: index 'SCHEMA.INDEX_NAME' or partition of such index is in unusable state
The ORA-01502 error occurs when Oracle attempts to use an index that has been marked as UNUSABLE. This typically happens after certain DDL operations, failed maintenance activities, or explicit marking. The error prevents queries and DML operations that require the index until it’s rebuilt.
Common Causes
Section titled “Common Causes”1. DDL Operations
Section titled “1. DDL Operations”ALTER TABLE MOVEwithout rebuilding indexesALTER TABLE SPLIT PARTITIONALTER TABLE MOVE PARTITIONALTER INDEX REBUILD PARTITIONfailure
2. Direct-Path Operations
Section titled “2. Direct-Path Operations”- SQL*Loader with DIRECT=TRUE
INSERT /*+ APPEND */operationsCREATE TABLE AS SELECTwith NOLOGGING
3. Explicit Marking
Section titled “3. Explicit Marking”ALTER INDEX index_name UNUSABLE- Index maintenance procedures
4. Partition Operations
Section titled “4. Partition Operations”- Adding/splitting/merging partitions
- Partition exchange operations
- Truncating partitions
5. Failed Operations
Section titled “5. Failed Operations”- Insufficient space during rebuild
- Interrupted maintenance jobs
- System crashes during DDL
Diagnostic Queries
Section titled “Diagnostic Queries”Find All Unusable Indexes
Section titled “Find All Unusable Indexes”-- Find unusable non-partitioned indexesSELECT owner, index_name, table_name, status, tablespace_nameFROM dba_indexesWHERE status = 'UNUSABLE'ORDER BY owner, table_name, index_name;
-- Find unusable index partitionsSELECT index_owner, index_name, partition_name, status, tablespace_nameFROM dba_ind_partitionsWHERE status = 'UNUSABLE'ORDER BY index_owner, index_name, partition_name;
-- Find unusable index subpartitionsSELECT index_owner, index_name, partition_name, subpartition_name, statusFROM dba_ind_subpartitionsWHERE status = 'UNUSABLE'ORDER BY index_owner, index_name, partition_name;Check Specific Index Status
Section titled “Check Specific Index Status”-- Detailed index informationSELECT i.owner, i.index_name, i.table_name, i.index_type, i.status, i.partitioned, i.tablespace_name, i.last_analyzed, i.num_rowsFROM dba_indexes iWHERE i.index_name = 'INDEX_NAME' AND i.owner = 'SCHEMA_NAME';
-- For partitioned index, check all partitionsSELECT partition_name, status, tablespace_name, num_rows, last_analyzedFROM dba_ind_partitionsWHERE index_name = 'INDEX_NAME' AND index_owner = 'SCHEMA_NAME'ORDER BY partition_position;Identify Impact
Section titled “Identify Impact”-- Find tables affected by unusable indexesSELECT DISTINCT i.owner, i.table_name, COUNT(*) as unusable_index_countFROM dba_indexes iWHERE i.status = 'UNUSABLE'GROUP BY i.owner, i.table_nameUNION ALLSELECT DISTINCT ip.index_owner, i.table_name, COUNT(*)FROM dba_ind_partitions ipJOIN dba_indexes i ON ip.index_owner = i.owner AND ip.index_name = i.index_nameWHERE ip.status = 'UNUSABLE'GROUP BY ip.index_owner, i.table_name;Resolution Steps
Section titled “Resolution Steps”1. Rebuild Non-Partitioned Index
Section titled “1. Rebuild Non-Partitioned Index”-- Basic rebuildALTER INDEX schema_name.index_name REBUILD;
-- Rebuild with optionsALTER INDEX schema_name.index_name REBUILD TABLESPACE new_tablespace PARALLEL 4 NOLOGGING;
-- Online rebuild (minimizes locking)ALTER INDEX schema_name.index_name REBUILD ONLINE;
-- After NOLOGGING rebuild, consider backupALTER INDEX schema_name.index_name LOGGING;2. Rebuild Partitioned Index
Section titled “2. Rebuild Partitioned Index”-- Rebuild single partitionALTER INDEX schema_name.index_nameREBUILD PARTITION partition_name;
-- Rebuild partition onlineALTER INDEX schema_name.index_nameREBUILD PARTITION partition_name ONLINE;
-- Rebuild all unusable partitions (generate script)SELECT 'ALTER INDEX ' || index_owner || '.' || index_name || ' REBUILD PARTITION ' || partition_name || ' ONLINE;'FROM dba_ind_partitionsWHERE status = 'UNUSABLE' AND index_owner = 'SCHEMA_NAME';3. Rebuild Subpartitioned Index
Section titled “3. Rebuild Subpartitioned Index”-- Rebuild single subpartitionALTER INDEX schema_name.index_nameREBUILD SUBPARTITION subpartition_name;
-- Generate script for all unusable subpartitionsSELECT 'ALTER INDEX ' || index_owner || '.' || index_name || ' REBUILD SUBPARTITION ' || subpartition_name || ';'FROM dba_ind_subpartitionsWHERE status = 'UNUSABLE' AND index_owner = 'SCHEMA_NAME';4. Rebuild All Unusable Indexes (Script Generator)
Section titled “4. Rebuild All Unusable Indexes (Script Generator)”-- Generate rebuild commands for all unusable indexesSET SERVEROUTPUT ONDECLARE v_sql VARCHAR2(4000);BEGIN -- Non-partitioned indexes FOR rec IN ( SELECT owner, index_name FROM dba_indexes WHERE status = 'UNUSABLE' ) LOOP v_sql := 'ALTER INDEX ' || rec.owner || '.' || rec.index_name || ' REBUILD ONLINE'; DBMS_OUTPUT.PUT_LINE(v_sql || ';'); END LOOP;
-- Partitioned indexes FOR rec IN ( SELECT index_owner, index_name, partition_name FROM dba_ind_partitions WHERE status = 'UNUSABLE' ) LOOP v_sql := 'ALTER INDEX ' || rec.index_owner || '.' || rec.index_name || ' REBUILD PARTITION ' || rec.partition_name || ' ONLINE'; DBMS_OUTPUT.PUT_LINE(v_sql || ';'); END LOOP;END;/5. Using DBMS_REPAIR for Corrupt Indexes
Section titled “5. Using DBMS_REPAIR for Corrupt Indexes”-- If index is corrupt, drop and recreate-- First, get the index DDLSELECT DBMS_METADATA.GET_DDL('INDEX', 'INDEX_NAME', 'SCHEMA_NAME')FROM dual;
-- Drop the indexDROP INDEX schema_name.index_name;
-- Recreate using the DDL obtained aboveCREATE INDEX schema_name.index_name ON schema_name.table_name(columns)TABLESPACE index_tablespaceONLINE;Handling Specific Scenarios
Section titled “Handling Specific Scenarios”After ALTER TABLE MOVE
Section titled “After ALTER TABLE MOVE”-- Move table to new tablespaceALTER TABLE schema_name.table_name MOVE TABLESPACE new_tablespace;
-- All indexes are now UNUSABLE - rebuild them-- For each index:ALTER INDEX schema_name.index_name REBUILD;
-- Or use a single statement to rebuild all (11g+)-- This rebuilds all indexes on the table:BEGIN FOR rec IN ( SELECT index_name FROM dba_indexes WHERE table_name = 'TABLE_NAME' AND owner = 'SCHEMA_NAME' ) LOOP EXECUTE IMMEDIATE 'ALTER INDEX SCHEMA_NAME.' || rec.index_name || ' REBUILD'; END LOOP;END;/After Partition Operations
Section titled “After Partition Operations”-- After SPLIT PARTITION-- Global indexes become unusable, local indexes for new partitions need rebuild
-- Rebuild global indexesSELECT 'ALTER INDEX ' || owner || '.' || index_name || ' REBUILD;'FROM dba_indexesWHERE table_name = 'PARTITIONED_TABLE' AND owner = 'SCHEMA_NAME' AND partitioned = 'NO' AND status = 'UNUSABLE';
-- To avoid this, use UPDATE INDEXES clauseALTER TABLE schema_name.table_nameSPLIT PARTITION old_part AT (value)INTO (PARTITION new_part1, PARTITION new_part2)UPDATE INDEXES; -- Maintains index validityFor Direct-Path Load Operations
Section titled “For Direct-Path Load Operations”-- Before direct-path load, you can skip index maintenanceALTER SESSION SET skip_unusable_indexes = TRUE;
-- Load data (indexes become unusable)-- SQL*Loader or INSERT /*+ APPEND */
-- After load, rebuild indexesALTER INDEX schema_name.index_name REBUILD;
-- Reset session parameterALTER SESSION SET skip_unusable_indexes = FALSE;Prevention Strategies
Section titled “Prevention Strategies”1. Use UPDATE INDEXES Clause
Section titled “1. Use UPDATE INDEXES Clause”-- Partition maintenance with index preservationALTER TABLE schema_name.table_nameMOVE PARTITION partition_nameTABLESPACE new_tablespaceUPDATE INDEXES;
ALTER TABLE schema_name.table_nameSPLIT PARTITION old_partition AT (split_value)INTO (PARTITION part1, PARTITION part2)UPDATE INDEXES;2. Use ONLINE Operations
Section titled “2. Use ONLINE Operations”-- Online table move (12c+)ALTER TABLE schema_name.table_name MOVE ONLINE;
-- Online partition moveALTER TABLE schema_name.table_nameMOVE PARTITION partition_name ONLINE;3. Monitoring Script
Section titled “3. Monitoring Script”-- Create monitoring jobCREATE OR REPLACE PROCEDURE check_unusable_indexes AS v_count NUMBER;BEGIN SELECT COUNT(*) INTO v_count FROM dba_indexes WHERE status = 'UNUSABLE';
SELECT COUNT(*) + v_count INTO v_count FROM dba_ind_partitions WHERE status = 'UNUSABLE';
IF v_count > 0 THEN -- Send alert or log INSERT INTO alert_log (alert_date, alert_type, alert_message) VALUES (SYSDATE, 'UNUSABLE_INDEX', v_count || ' unusable indexes detected'); COMMIT; END IF;END;/
-- Schedule daily checkBEGIN DBMS_SCHEDULER.CREATE_JOB( job_name => 'CHECK_UNUSABLE_INDEXES', job_type => 'STORED_PROCEDURE', job_action => 'check_unusable_indexes', repeat_interval => 'FREQ=DAILY; BYHOUR=6', enabled => TRUE );END;/4. Session Parameter for Bulk Operations
Section titled “4. Session Parameter for Bulk Operations”-- Allow DML to skip unusable indexes (use carefully)ALTER SESSION SET skip_unusable_indexes = TRUE;
-- Perform bulk operations-- ...
-- Rebuild indexes afterward-- ...
-- ResetALTER SESSION SET skip_unusable_indexes = FALSE;Troubleshooting
Section titled “Troubleshooting”Insufficient Space for Rebuild
Section titled “Insufficient Space for Rebuild”-- Check available spaceSELECT tablespace_name, ROUND(SUM(bytes)/1024/1024, 2) as free_mbFROM dba_free_spaceWHERE tablespace_name = 'INDEX_TABLESPACE'GROUP BY tablespace_name;
-- Estimate index sizeSELECT ROUND(SUM(bytes)/1024/1024, 2) as current_size_mbFROM dba_segmentsWHERE segment_name = 'INDEX_NAME' AND owner = 'SCHEMA_NAME';
-- If space is tight, rebuild to different tablespaceALTER INDEX schema_name.index_nameREBUILD TABLESPACE different_tablespace;Long-Running Rebuild
Section titled “Long-Running Rebuild”-- Monitor rebuild progressSELECT sid, serial#, opname, target, sofar, totalwork, ROUND(sofar/NULLIF(totalwork,0)*100, 2) as pct_complete, time_remainingFROM v$session_longopsWHERE opname LIKE '%Index%' AND sofar < totalwork;
-- Use parallel for faster rebuildALTER INDEX schema_name.index_name REBUILD PARALLEL 8;
-- Reset parallelism after rebuildALTER INDEX schema_name.index_name NOPARALLEL;Cannot Rebuild Due to Locks
Section titled “Cannot Rebuild Due to Locks”-- Check for locks on the tableSELECT l.session_id, s.serial#, s.username, l.lock_type, l.mode_held, l.mode_requested, l.blocking_othersFROM dba_lock lJOIN v$session s ON l.session_id = s.sidWHERE l.lock_type IN ('DML', 'DDL') AND l.owner = 'SCHEMA_NAME';
-- Use ONLINE rebuild to minimize lock timeALTER INDEX schema_name.index_name REBUILD ONLINE;Related Errors
Section titled “Related Errors”- ORA-01578 - Oracle data block corrupted
- ORA-01653 - Unable to extend table
- ORA-01654 - Unable to extend index
- ORA-14086 - Partition maintenance operation
Best Practices Summary
Section titled “Best Practices Summary”- Always rebuild indexes after
ALTER TABLE MOVE - Use
UPDATE INDEXESclause for partition operations - Prefer ONLINE rebuilds to minimize downtime
- Monitor for unusable indexes regularly
- Use PARALLEL for large index rebuilds, then reset to NOPARALLEL
- Consider
skip_unusable_indexesfor bulk operations, but rebuild promptly - Test partition maintenance procedures in non-production first