Exploring ORA-01502 error and why we usually get this error message. I am not explaining why/how the index status changed to unusable( mostly due to the table move and alter index xxxx unusable ..)
You will get
ORA-01502: index or partition of such index is in unusable state
If you have the parameter skip_unusable_indexes= false then it makes sense that oracle reported this error during DML activity.
If you don't care about the unusable indexes during DML and you want the optimizer to choose different(expensive) execution plans during SELECT then you can set the parameter skip_unusable_indexes= true at the instance level or at the session level and move on.
what if you got this error even when you have the paramater skip_unusable_indexes set to true at the instance level..
i.e
SQL> show parameter skip
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
skip_unusable_indexes boolean TRUE
SKIP_UNUSABLE_INDEXES enables or disables the use and reporting of tables with unusable indexes or index partitions. If a SQL statement uses a hint that forces the usage of an unusable index, then this hint takes precedence over initialization parameter settings, including SKIP_UNUSABLE_INDEXES. If the optimizer chooses an unusable index, then an ORA-01502 error will result. (See Oracle Database Administrator's Guide for more information about using hints.)
Values: true
Disables error reporting of indexes and index partitions marked UNUSABLE. This setting allows all operations (inserts, deletes, updates, and selects) on tables with unusable indexes or index partitions.
and you still got the error during DML activity on this table, why?
SQL> insert into skip_index (salesrep_dim_pk) values (55555);
insert into skip_index (salesrep_dim_pk) values (55555)
*
ERROR at line 1:
ORA-01502: index 'SYS.SKIP_INDEX_UNIQUE' or partition of such index is in unusable state
Example:
SQL> create table TEST_TABLE as select * from cn.cn_d_salesreps;
Table created.
SQL> CREATE UNIQUE INDEX TEST_INDEX_UNIQUE ON SKIP_INDEX (SALESREP_DIM_PK);
Index created.
SQL> commit;
Commit complete.
SQL> select sum(bytes) from dba_segments where segment_name='SKIP_INDEX_UNIQUE';
SUM(BYTES)
----------
196608
SQL> select * from TEST_TABLE where salesrep_dim_pk =95056;
Execution Plan
----------------------------------------------------------
Plan hash value: 684699485
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1470 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| TEST_TABLE | 1 | 1470 | 2 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | TEST_INDEX_UNIQUE | 1 | | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("SALESREP_DIM_PK"=95056)
Above explain plan shows the optimizer is using the index.
Now insert some data into the data..some DML...
SQL> insert into skip_index (salesrep_dim_pk) values (55555);
1 row created.
SQL> delete from skip_index where salesrep_dim_pk=55555;
1 row deleted.
SQL> commit;
Commit complete.
****** Now mark the index UNUSABLE *****
SQL> alter index TEST_INDEX_UNIQUE unusable;
Index altered.
SQL> select sum(bytes) from dba_segments where segment_name='TEST_INDEX_UNIQUE';
SUM(BYTES)
----------
196608
***** This is bad even though the index is unusable the segments were not dropped for the index ***
****The default behaviour is oracle drops the segments for an unusable index *****
SQL> select * from skip_index where salesrep_dim_pk =95056;
Execution Plan
----------------------------------------------------------
Plan hash value: 74755328
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 151 | 216K| 10 (0)| 00:00:01 |
|* 1 | TABLE ACCESS STORAGE FULL| TEST_TABLE | 151 | 216K| 10 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - storage("SALESREP_DIM_PK"=95056)
filter("SALESREP_DIM_PK"=95056)
****As expected the access path changed from INDEX to FULL TABLE SCAN as the index is unusable******
***** try doing some DML ********
SQL> insert into TEST_TABLE (salesrep_dim_pk) values (55555);
insert into TEST_TABLE (salesrep_dim_pk) values (55555)
*
ERROR at line 1:
ORA-01502: index 'TEST_INDEX_UNIQUE' or partition of such index is in unusable state
***Since the index is a non unique index oracle will not allows us to do any dml activity on the underlying table
on which the index is unusable and will not drop the index segments either ****
Note:
If an index is used to enforce a UNIQUE constraint on a table, then allowing insert and update operations on the table might violate the constraint. Therefore, this setting does not disable error reporting for unusable indexes that are unique.
SQL> alter index TEST_INDEX_UNIQUE rebuild;
Index altered.
SQL> select sum(bytes) from dba_segments where segment_name='TEST_INDEX_UNIQUE';
SUM(BYTES)
----------
196608
SQL> insert into TEST_TABLE (salesrep_dim_pk) values (55555);
1 row created.
SQL> commit;
Commit complete.
where as non unique unusable index doesn't throw this error.
Create table TEST_TABLE as select * from my_table;
****Created non-unique index here ********
create index TEXT_IDX on TEST_TABLE(customer_num,customer_name);
commit;
SQL> select customer_num from TEST_TABLE where customer_num='8.1502701000322E15';
Execution Plan
----------------------------------------------------------
Plan hash value: 3224766131
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 12 | 96 | 4 (0)| 00:00:01 |
|* 1 | INDEX RANGE SCAN| TEXT_IDX | 12 | 96 | 4 (0)| 00:00:01 |
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("CUSTOMER_NUM"=8150270100032200)
SQL> select wo_num from TEST_TABLE where wo_num='1.00037856314112E15';
Execution Plan
----------------------------------------------------------
Plan hash value: 3979868219
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 7 | 49 | 317K (2)| 00:16:09 |
|* 1 | TABLE ACCESS STORAGE FULL| TEST_TABLE | 7 | 49 | 317K (2)| 00:16:09 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - storage("WO_NUM"=1000378563141120)
filter("WO_NUM"=1000378563141120)
SQL> ALTER INDEX TEXT_IDX unusable;
Index altered.
SQL> select index_name,status from dba_indexes where index_name='TEXT_IDX';
INDEX_NAME STATUS
------------------------------ --------
TEXT_IDX UNUSABLE
SQL> select sum(bytes)/1024/1024/1024 from dba_segments where segment_name='TEXT_IDX';
SUM(BYTES)/1024/1024/1024
-------------------------
SQL> exec dbms_stats.gather_table_stats('MYSCHEMA','TEST_TABLE',estimate_percent=>dbms_stats.auto_sample_size,method_opt=>'FOR ALL COLUMNS SIZE 1',cascade=>TRUE);
PL/SQL procedure successfully completed.
SQL> update TEST_TABLE set customer_num='1234567890' where customer_num='8.1502701000322E15';
6 rows updated.
SQL> alter index TExt_idx rebuild;
Index altered.
SQL> SQL>
SQL> commit;
Commit complete.
SQL> select sum(bytes)/1024/1024/1024 from dba_segments where segment_name='TEXT_IDX';
SUM(BYTES)/1024/1024/1024
-------------------------
2.00976563
You will get
ORA-01502: index or partition of such index is in unusable state
If you have the parameter skip_unusable_indexes= false then it makes sense that oracle reported this error during DML activity.
If you don't care about the unusable indexes during DML and you want the optimizer to choose different(expensive) execution plans during SELECT then you can set the parameter skip_unusable_indexes= true at the instance level or at the session level and move on.
what if you got this error even when you have the paramater skip_unusable_indexes set to true at the instance level..
i.e
SQL> show parameter skip
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
skip_unusable_indexes boolean TRUE
SKIP_UNUSABLE_INDEXES enables or disables the use and reporting of tables with unusable indexes or index partitions. If a SQL statement uses a hint that forces the usage of an unusable index, then this hint takes precedence over initialization parameter settings, including SKIP_UNUSABLE_INDEXES. If the optimizer chooses an unusable index, then an ORA-01502 error will result. (See Oracle Database Administrator's Guide for more information about using hints.)
Values: true
Disables error reporting of indexes and index partitions marked UNUSABLE. This setting allows all operations (inserts, deletes, updates, and selects) on tables with unusable indexes or index partitions.
and you still got the error during DML activity on this table, why?
SQL> insert into skip_index (salesrep_dim_pk) values (55555);
insert into skip_index (salesrep_dim_pk) values (55555)
*
ERROR at line 1:
ORA-01502: index 'SYS.SKIP_INDEX_UNIQUE' or partition of such index is in unusable state
Example:
SQL> create table TEST_TABLE as select * from cn.cn_d_salesreps;
Table created.
SQL> CREATE UNIQUE INDEX TEST_INDEX_UNIQUE ON SKIP_INDEX (SALESREP_DIM_PK);
Index created.
SQL> commit;
Commit complete.
SQL> select sum(bytes) from dba_segments where segment_name='SKIP_INDEX_UNIQUE';
SUM(BYTES)
----------
196608
SQL> select * from TEST_TABLE where salesrep_dim_pk =95056;
Execution Plan
----------------------------------------------------------
Plan hash value: 684699485
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1470 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| TEST_TABLE | 1 | 1470 | 2 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | TEST_INDEX_UNIQUE | 1 | | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("SALESREP_DIM_PK"=95056)
Above explain plan shows the optimizer is using the index.
Now insert some data into the data..some DML...
SQL> insert into skip_index (salesrep_dim_pk) values (55555);
1 row created.
SQL> delete from skip_index where salesrep_dim_pk=55555;
1 row deleted.
SQL> commit;
Commit complete.
****** Now mark the index UNUSABLE *****
SQL> alter index TEST_INDEX_UNIQUE unusable;
Index altered.
SQL> select sum(bytes) from dba_segments where segment_name='TEST_INDEX_UNIQUE';
SUM(BYTES)
----------
196608
***** This is bad even though the index is unusable the segments were not dropped for the index ***
****The default behaviour is oracle drops the segments for an unusable index *****
SQL> select * from skip_index where salesrep_dim_pk =95056;
Execution Plan
----------------------------------------------------------
Plan hash value: 74755328
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 151 | 216K| 10 (0)| 00:00:01 |
|* 1 | TABLE ACCESS STORAGE FULL| TEST_TABLE | 151 | 216K| 10 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - storage("SALESREP_DIM_PK"=95056)
filter("SALESREP_DIM_PK"=95056)
****As expected the access path changed from INDEX to FULL TABLE SCAN as the index is unusable******
***** try doing some DML ********
SQL> insert into TEST_TABLE (salesrep_dim_pk) values (55555);
insert into TEST_TABLE (salesrep_dim_pk) values (55555)
*
ERROR at line 1:
ORA-01502: index 'TEST_INDEX_UNIQUE' or partition of such index is in unusable state
***Since the index is a non unique index oracle will not allows us to do any dml activity on the underlying table
on which the index is unusable and will not drop the index segments either ****
Note:
If an index is used to enforce a UNIQUE constraint on a table, then allowing insert and update operations on the table might violate the constraint. Therefore, this setting does not disable error reporting for unusable indexes that are unique.
SQL> alter index TEST_INDEX_UNIQUE rebuild;
Index altered.
SQL> select sum(bytes) from dba_segments where segment_name='TEST_INDEX_UNIQUE';
SUM(BYTES)
----------
196608
SQL> insert into TEST_TABLE (salesrep_dim_pk) values (55555);
1 row created.
SQL> commit;
Commit complete.
where as non unique unusable index doesn't throw this error.
Create table TEST_TABLE as select * from my_table;
****Created non-unique index here ********
create index TEXT_IDX on TEST_TABLE(customer_num,customer_name);
commit;
SQL> select customer_num from TEST_TABLE where customer_num='8.1502701000322E15';
Execution Plan
----------------------------------------------------------
Plan hash value: 3224766131
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 12 | 96 | 4 (0)| 00:00:01 |
|* 1 | INDEX RANGE SCAN| TEXT_IDX | 12 | 96 | 4 (0)| 00:00:01 |
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("CUSTOMER_NUM"=8150270100032200)
SQL> select wo_num from TEST_TABLE where wo_num='1.00037856314112E15';
Execution Plan
----------------------------------------------------------
Plan hash value: 3979868219
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 7 | 49 | 317K (2)| 00:16:09 |
|* 1 | TABLE ACCESS STORAGE FULL| TEST_TABLE | 7 | 49 | 317K (2)| 00:16:09 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - storage("WO_NUM"=1000378563141120)
filter("WO_NUM"=1000378563141120)
SQL> ALTER INDEX TEXT_IDX unusable;
Index altered.
SQL> select index_name,status from dba_indexes where index_name='TEXT_IDX';
INDEX_NAME STATUS
------------------------------ --------
TEXT_IDX UNUSABLE
SQL> select sum(bytes)/1024/1024/1024 from dba_segments where segment_name='TEXT_IDX';
SUM(BYTES)/1024/1024/1024
-------------------------
SQL> exec dbms_stats.gather_table_stats('MYSCHEMA','TEST_TABLE',estimate_percent=>dbms_stats.auto_sample_size,method_opt=>'FOR ALL COLUMNS SIZE 1',cascade=>TRUE);
PL/SQL procedure successfully completed.
SQL> update TEST_TABLE set customer_num='1234567890' where customer_num='8.1502701000322E15';
6 rows updated.
SQL> alter index TExt_idx rebuild;
Index altered.
SQL> SQL>
SQL> commit;
Commit complete.
SQL> select sum(bytes)/1024/1024/1024 from dba_segments where segment_name='TEXT_IDX';
SUM(BYTES)/1024/1024/1024
-------------------------
2.00976563