Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question.

Asked: April 04, 2002 - 8:37 pm UTC

Last updated: August 29, 2012 - 11:23 am UTC

Version: 8.1.6

Viewed 50K+ times! This question is

You Asked

Why is an index listed as 'VALID' in dba_objects, but can be listed as 'UNUSABLE' in dba_indexes (i.e. if the table was just moved)

Doesn't it seem like these status should be in sync?


and Tom said...

dba_objects just has:

decode(o.status, 0, 'N/A', 1, 'VALID', 'INVALID'),

whereas dba_indexes has:

decode(bitand(i.property, 2), 2, 'N/A',
decode(bitand(i.flags, 1), 1, 'UNUSABLE',
decode(bitand(i.flags, 8), 8, 'INPROGRS',
'VALID'))),

dba_objects is a generic view, having status in there today is probably "obsolete" as so much stuff has been added since this view was originally created. UNUSABLE is a relatively new attribute of an index.

DBA_OBJECTS must be for every object -- very generic.
DBA_INDEXES -- only for indexes, very specific.

Rating

  (16 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

A reader, April 05, 2002 - 1:57 pm UTC

Thanks for your answer.

You mentioned the status in dba_objects is probably 'obsolete'.

Can you recommend a better way to verify the validity of the objects in the database? It seems you can really only get a true status from dba_objects for things like triggers and procedures...

Also, for trivia's sake, what tables are you querying with your decode statements below?

Thanks again!

Tom Kyte
April 05, 2002 - 2:39 pm UTC

goto each object type table (dba_tables, dba_indexes) and look there.


The decodes are from the view definitions - select text from all_views where view_name = 'xxxxx';

make sure the set long 50000 in sqlplus first

skip_unusable_indexes=true

A reader, February 19, 2003 - 12:44 pm UTC

Shouldn't skip_unusable_indexes prevent this error ? 

SQL> analyze table ism_lat estimate statistics

analyze table ism_lat estimate statistics
*
ERROR at line 1:
ORA-01502: index 'REL.SYS_C001218' or partition of such index is in unusable
state


SQL> alter session set skip_unusable_indexes=true;

Session altered.

SQL> analyze table ism_lat estimate statistics;
analyze table ism_lat estimate statistics
*
ERROR at line 1:
ORA-01502: index 'REL.SYS_C001218' or partition of such index is in unusable
state                      

Tom Kyte
February 19, 2003 - 3:34 pm UTC

<quote>
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.
</quote>

so no, because you are doing an analyze and your analyze is equivalent to

analyze table t estimate statistics
for table
for all indexes
for all columns


you should use dbms_stats (won't fix this issue, but you should) and you should look into whether you want/need/desire the histograms (they are costly to compute) and if not, maybe you can turn that estimate into COMPUTE and get better stats.

Can you give me an example to make an index Invalid ?

David Jiang, June 05, 2003 - 4:49 pm UTC

Tom,

Thank you for the web site, which relly give me a lot of help.

I see that you have a column Status in User_indexes. But it seems to me all indexes is either created valid or not created at all. The index created on the table x is gone if the table x is dropped. It seems that the Status columna is useless here in user_indexes. Is this true?

Please help me to verify it.

Thanks a lot.


Tom Kyte
June 05, 2003 - 6:30 pm UTC

what about unusable?
or inprogrs?
or N/A?



Excellent

David Jiang, June 06, 2003 - 3:18 pm UTC

Thanks a lot, Tom.

OK, I see it now. Can you provide examples of valid/inprogress/ n/a / usable or unusable regarding index status. I want to know how to make the status showed in all these different condition.

Thanks again for your prompt answer.

Tom Kyte
June 06, 2003 - 4:07 pm UTC

valid -- just create one

unusable -- alter index iname unusable;


If you do "shutdown abort" while a CREATE INDEX ... indextype ctxsys.ConText job is running (which otherwise would've completed without error) then you can get... STATUS = 'INPROGRS'





local index becomming unusable,

sns, February 21, 2005 - 10:59 am UTC

We have a range partitioned table and couple of local indexes defined on them.
Recently we split the partition at the MAXVALUE for the business reasons.
Two things happened:
The local index got split too (which is good) however, the status became UNUSABLE.
The tablespace used to store new partition of the index was different than other partitions(I wanted to store in the same tablespace like other partitions).

I did a small test by creating a partition table and a local index on that. According to that test, the local index DID NOT became UNUSABLE after I split the table partition and the new partition was stored in the right tablespace.

Why my test table and index behaved differently than my production table and index?





Tom Kyte
February 21, 2005 - 12:46 pm UTC

so, show us the commands you used to reproduce -- the entire test case.

if the partition has data in it, I would expect the local index to go unusable (until 10g when there is an option to maintain local indexes just like global indexes has in 9i)

sns, February 21, 2005 - 2:01 pm UTC

May be I did not had any values in the MAXVALUE bucket when I split.

I noticed the local index became unusable when I had data in the MAXVALUE bucket.

Is this something inevitable? I am using 10.1.0.3.

The theory is:
Rebuild the local index after I split the table partition.
Rebuild the global partitioned index after the tab partition is split.
Rebuild the non-partitioned index???

Thanks

Tom Kyte
February 21, 2005 - 3:47 pm UTC

did you use the

UPDATE INDEXES

clause -- if you do, it is not inevitable.

unusable index

sns, February 21, 2005 - 4:52 pm UTC

I did not use UPDATE INDEXES clause.
Well, I am not sure where this UPDATE INDEXES clause comes into picture. Does this apply to all the 3 types of Indexes (local, global partitioned and non-partitioned)?

Is this beneficial than rebuilding the index partition after it becomes UNUSABLE?

Thanks

Tom Kyte
February 22, 2005 - 8:00 am UTC

if (you do not want to have downtime due to unusable indexes)
then
   update indexes will do the inserts/deletes to move stuff around 
   in the index, causing the ALTER to take longer from the perspective
   of the person doing the alter but not causing "downtime" from the 
   perspective of the people running the queries
else
   don't use update indexes and let them go unusable and rebuild them afterwards.
   overall this ddl will tend to run faster but you will have time when the data
   is basically "not available" if you wanted to access it via those indexes
end if

it is a trade off -- continous availability, vs, raw speed of the operation.  

You can always check out the sql reference guide for the syntax, but "update indexes" maintains local and global, consider:

ops$tkyte@ORA10G> CREATE TABLE t
  2  (
  3    temp_date  date,
  4    x          int,
  5    y int
  6  )
  7  PARTITION BY RANGE (temp_date)
  8  (
  9    PARTITION part1 VALUES LESS THAN (to_date('13-mar-2003','dd-mon-yyyy')),
 10    PARTITION part2 VALUES LESS THAN (to_date('14-mar-2003','dd-mon-yyyy')),
 11    PARTITION junk VALUES LESS THAN (MAXVALUE)
 12  )
 13  /
 
Table created.
 
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> create index t_idx1 on t(x) global;
 
Index created.
 
ops$tkyte@ORA10G> create index t_idx2 on t(y) local;
 
Index created.
 
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> insert into t select to_date('10-mar-2003')+rownum, rownum, rownum from all_users where rownum <= 10;
 
10 rows created.
 
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> select index_name, partition_name, status from user_ind_partitions where index_name like 'T_IDX_';
 
INDEX_NAME                     PARTITION_NAME                 STATUS
------------------------------ ------------------------------ --------
T_IDX2                         JUNK                           USABLE
T_IDX2                         PART1                          USABLE
T_IDX2                         PART2                          USABLE
 
ops$tkyte@ORA10G> select index_name, status from user_indexes where table_name = 'T' ;
 
INDEX_NAME                     STATUS
------------------------------ --------
T_IDX1                         VALID
T_IDX2                         N/A
 
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> alter table t split partition junk at (to_date('15-mar-2003','dd-mon-yyyy'))
  2  into ( partition part3, partition junk);
 
Table altered.
 
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> select index_name, partition_name, status from user_ind_partitions where index_name like 'T_IDX_';
 
INDEX_NAME                     PARTITION_NAME                 STATUS
------------------------------ ------------------------------ --------
T_IDX2                         JUNK                           UNUSABLE
T_IDX2                         PART1                          USABLE
T_IDX2                         PART2                          USABLE
T_IDX2                         PART3                          UNUSABLE
 
ops$tkyte@ORA10G> select index_name, status from user_indexes where table_name = 'T' ;
 
INDEX_NAME                     STATUS
------------------------------ --------
T_IDX1                         UNUSABLE
T_IDX2                         N/A
 
<b>that is what happens when no index maintainence is done. the global goes unusable and the affected partitions for local indexes do as well, but</b>


ops$tkyte@ORA10G>
ops$tkyte@ORA10G> alter index t_idx1 rebuild;
 
Index altered.
 
ops$tkyte@ORA10G> alter index t_idx2 rebuild partition part3;
 
Index altered.
 
ops$tkyte@ORA10G> alter index t_idx2 rebuild partition junk;
 
Index altered.
 
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> select index_name, partition_name, status from user_ind_partitions where index_name like 'T_IDX_';
 
INDEX_NAME                     PARTITION_NAME                 STATUS
------------------------------ ------------------------------ --------
T_IDX2                         JUNK                           USABLE
T_IDX2                         PART1                          USABLE
T_IDX2                         PART2                          USABLE
T_IDX2                         PART3                          USABLE
 
ops$tkyte@ORA10G> select index_name, status from user_indexes where table_name = 'T' ;
 
INDEX_NAME                     STATUS
------------------------------ --------
T_IDX1                         VALID
T_IDX2                         N/A
 
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> alter table t split partition junk at (to_date('16-mar-2003','dd-mon-yyyy'))
  2  into ( partition part4, partition junk) <b>update indexes;
 </b>
Table altered.
 
<b>using the new update indexes in 10g, you get all indexes maintained:</b>

ops$tkyte@ORA10G>
ops$tkyte@ORA10G> select index_name, partition_name, status from user_ind_partitions where index_name like 'T_IDX_';
 
INDEX_NAME                     PARTITION_NAME                 STATUS
------------------------------ ------------------------------ --------
T_IDX2                         JUNK                           USABLE
T_IDX2                         PART1                          USABLE
T_IDX2                         PART2                          USABLE
T_IDX2                         PART3                          USABLE
T_IDX2                         PART4                          USABLE
 
ops$tkyte@ORA10G> select index_name, status from user_indexes where table_name = 'T' ;
 
INDEX_NAME                     STATUS
------------------------------ --------
T_IDX1                         VALID
T_IDX2                         N/A
 
 

splitting table with 10g

Miroslav Siket, June 20, 2005 - 3:13 am UTC

Hello Tom,

I am using Oracle 10g and I have big tables with local indexes where the split operation takes several seconds. An example operation would be:
alter table MY_TABLE split partition MAX_VALUE at (123456789) into (partition MY_OLD_DATA, partition MAX_VALUE) UPDATE INDEXES;

I am using UPDATE INDEXES in the split statement as you suggest with 10g, but if at the same time some other application tries to insert data into the given table I get the error:
ORA-01502: index 'MY_INDEX' or partition of such index is in unable state

Of course, when the split finishes, everything is all right, all indexes are usable again, but during the time of split, indexes are unusable. I was able to run the following query:
select * from user_ind_partitions where status != "USABLE"
duting the split and I was able to find index partitions of my table among unusable while the operation lasted.

Isn't that supposed to work transparently for the applications? How can I prevent this situation to occur or how should I go about it? I need to have this database available all the time.

Tom Kyte
June 20, 2005 - 10:01 am UTC

you are doing DDL to the partition, it is the DDL on the partition that is in fact the "culprit" here. The update indexes is to preserve the ability to read the object. But during the DDL operation, you are not going to modify that partition (it is being modified itself, it is like you are doing a massive update in a way, rewriting the entire partition)

Are you doing this frequently? you could instead not use max value and just add partitions at the end over time maybe?

table split in 10g

Miroslav Siket, June 21, 2005 - 3:03 am UTC

Hello Tom,

I have imagined this would be the cause. In 9i (on another server) I pre-create partitions instead of splitting the MAXVALUE one. There is everything all right as at the time when I add a partition, it is well ahead of time and no application tries to do any operation on it (naturaly). The fields that I have the split to be made on is the unix timestamp and I have the data that are only added (monitoring data). Thus, in 9i I precreate partitions every day (several millions of entries per day) and I compress the old partitions. The current partition (where DMLs are done) is untouched by any DDL. The only disadvantage of this approach is that I have to pre-create enough of partitions ahead of time. If something fails there, no data will be inserted - i.e. when I fail to precreate partitions for some time. On the other hand, with MAXVALUE partitions I thought I could do these kind of operations post mortem (at least in 10g) - i.e. at any time I can schedule that and at no time I would endanger the applications. Would locking of the table for the time of the split allow this?

Tom Kyte
June 21, 2005 - 4:35 pm UTC

<quote>
Would locking of the table for the time of the split allow this?
</quote>

it would prevent the insertions, just the same case you have now!

A straight question...

A reader, September 02, 2005 - 11:21 am UTC

How can you see if an index is a local or global?

Tom Kyte
September 03, 2005 - 7:28 am UTC

ops$tkyte@ORA10G> CREATE TABLE t
  2  (
  3    dt  date,
  4    x   int,
  5    y   varchar2(25)
  6  )
  7  PARTITION BY RANGE (dt)
  8  (
  9    PARTITION part1 VALUES LESS THAN (to_date('13-mar-2003','dd-mon-yyyy')) ,
 10    PARTITION part2 VALUES LESS THAN (to_date('14-mar-2003','dd-mon-yyyy')) ,
 11    PARTITION junk VALUES LESS THAN (MAXVALUE)
 12  )
 13  /
 
Table created.
 
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> create index t_local on t(x) local;
 
Index created.
 
ops$tkyte@ORA10G> create index t_global on t(y) global
  2  partition by range(y)
  3  ( partition p1 values less than ( 'X' ),
  4    partition p2 values less than (MAXVALUE)
  5  );
 
Index created.
 
ops$tkyte@ORA10G>
ops$tkyte@ORA10G> select index_name, locality
  2    from user_part_indexes
  3   where table_name = 'T';
 
INDEX_NAME                     LOCALI
------------------------------ ------
T_LOCAL                        LOCAL
T_GLOBAL                       GLOBAL
 
 

A straight question...

David Martin, September 02, 2005 - 11:22 am UTC

How can you see if an index is a local or global?

Thanks!

Dave, September 05, 2005 - 4:41 am UTC


Useful - to skip unused Index

Surangani, October 07, 2005 - 5:16 am UTC

As a DB user, i was querying some data from a table which ia partitioned.So the query stmt returns error that the index is not in use.After referring this site, I was able to skip the unusable index(skip_unusable_indexes) and then run the query, which retrieved data.

Split partition containing data doesn't always make the index partitions unusable...

Dawn, May 01, 2007 - 12:03 pm UTC

Hi Tom,

We had an error in our production database today whereby two partitions on each of the local binary indexes of a partitioned table were marked as invalid. The reason why is partly to do with not having "update indexes" after the split partition request and partly because there was data in the partition being split, but further investigation proved interesting.

I searched for information on local indexes going unusable following a split, and came up with the following: http://download-uk.oracle.com/docs/cd/B19306_01/server.102/b14231/partiti.htm#i1008028

"If the partition you are splitting contains any data, indexes may be marked UNUSABLE as explained in the following table..."

Thing is, I know that the (catchall) partition being split every evening contains data quite regularly, but this error doesn't crop up very often! Looking more closely, I found that if the data stayed entirely within one of the two new partitions, the indexes are fine. If, however, the data is moved into both partitions, then the corresponding index partitions become unusable, as this test case shows:



Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.2.0 - 64bit Production
With the Partitioning and Data Mining options

SQL> CREATE TABLE dawn_test_2 (col1 DATE)
  2  PARTITION BY RANGE (col1) 
  3  (  
  4    PARTITION PARTNO_01 VALUES LESS THAN (TO_DATE(' 2007-04-19 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')),
  5    PARTITION PARTNO_OTHER VALUES LESS THAN (MAXVALUE));

Table created.

SQL> CREATE BITMAP INDEX dawn_test2_bind ON DAWN_TEST_2
  2  (col1) LOCAL ;

Index created.

SQL>  INSERT INTO dawn_test_2 VALUES (TO_DATE('20/04/2007', 'dd/mm/yyyy'));

1 row created.

SQL> COMMIT;

Commit complete.

SQL> SELECT * FROM dawn_test_2 PARTITION (partno_other);

COL1
---------
20-APR-07

SQL> ALTER TABLE dawn_test_2 SPLIT PARTITION PARTNO_OTHER AT (TO_DATE('20/04/2007', 'dd/mm/yyyy')) 
  2  INTO (PARTITION partno_02, PARTITION PARTNO_OTHER);

Table altered.

SQL> SELECT * FROM dawn_test_2 PARTITION (partno_other);

COL1
---------
20-APR-07

SQL> SELECT index_name, partition_name, status FROM user_ind_partitions
  2  WHERE index_name = 'DAWN_TEST2_BIND';

INDEX_NAME                     PARTITION_NAME                 STATUS
------------------------------ ------------------------------ --------
DAWN_TEST2_BIND                PARTNO_01                      USABLE
DAWN_TEST2_BIND                PARTNO_02                      USABLE
DAWN_TEST2_BIND                PARTNO_OTHER                   USABLE

SQL> ALTER TABLE dawn_test_2 SPLIT PARTITION PARTNO_OTHER AT (TO_DATE('21/04/2007', 'dd/mm/yyyy')) 
  2  INTO (PARTITION partno_03, PARTITION PARTNO_OTHER);

Table altered.

SQL> SELECT * FROM dawn_test_2 PARTITION (partno_other);

no rows selected

SQL> SELECT index_name, partition_name, status FROM user_ind_partitions
  2  WHERE index_name = 'DAWN_TEST2_BIND';

INDEX_NAME                     PARTITION_NAME                 STATUS
------------------------------ ------------------------------ --------
DAWN_TEST2_BIND                PARTNO_01                      USABLE
DAWN_TEST2_BIND                PARTNO_02                      USABLE
DAWN_TEST2_BIND                PARTNO_03                      USABLE
DAWN_TEST2_BIND                PARTNO_OTHER                   USABLE

SQL>   
SQL> INSERT INTO dawn_test_2 VALUES (TO_DATE('21/04/2007', 'dd/mm/yyyy'));

1 row created.

SQL> INSERT INTO dawn_test_2 VALUES (TO_DATE('22/04/2007', 'dd/mm/yyyy'));

1 row created.

SQL> COMMIT;

Commit complete.

SQL> SELECT * FROM dawn_test_2 PARTITION (partno_other);

COL1
---------
21-APR-07
22-APR-07

SQL> 
SQL> ALTER TABLE dawn_test_2 SPLIT PARTITION PARTNO_OTHER AT (TO_DATE('22/04/2007', 'dd/mm/yyyy')) 
  2  INTO (PARTITION partno_04, PARTITION PARTNO_OTHER);

Table altered.

SQL> SELECT * FROM dawn_test_2 PARTITION (partno_other);

COL1
---------
22-APR-07

SQL> SELECT index_name, partition_name, status FROM user_ind_partitions
  2  WHERE index_name = 'DAWN_TEST2_BIND';

INDEX_NAME                     PARTITION_NAME                 STATUS
------------------------------ ------------------------------ --------
DAWN_TEST2_BIND                PARTNO_01                      USABLE
DAWN_TEST2_BIND                PARTNO_02                      USABLE
DAWN_TEST2_BIND                PARTNO_03                      USABLE
DAWN_TEST2_BIND                PARTNO_04                      UNUSABLE
DAWN_TEST2_BIND                PARTNO_OTHER                   UNUSABLE


Is this always the case, or are there times when splitting a partition where the data stays entirely within one of the new partitions cause the local index partitions to go unusable?

Unusable Index

Rajeshwaran, Jeyabal, October 08, 2011 - 11:11 pm UTC

Tom:

I was reading Chapter#2 on Effective Oracle by design

<quote>
If you don’t ever drop the index, but set it to UNUSABLE instead, you can never lose the index. If the command to reenable
the index fails, when the users run queries that need that index, they will get an error message, which, of course,
they will report immediately. So, rather than having the system suffer from mysterious performance issues for hours or days, the DBA will get a call early on and fix the problem
</quote>

rajesh@ORA10GR2> create index t_ind on t(object_id) nologging;

Index created.

Elapsed: 00:00:00.06
rajesh@ORA10GR2> alter index t_ind unusable;

Index altered.

Elapsed: 00:00:00.06
rajesh@ORA10GR2>
rajesh@ORA10GR2> select object_name from t where object_id = 5;

OBJECT_NAME
------------------------------
CLU$

Elapsed: 00:00:00.01

Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |    22 |   168   (2)| 00:00:03 |
|*  1 |  TABLE ACCESS FULL| T    |     1 |    22 |   168   (2)| 00:00:03 |
--------------------------------------------------------------------------


I don't see any error message while running queries that need an Index. Is that something got changed with Oracle 10G ?
Tom Kyte
October 09, 2011 - 11:13 am UTC

Things change over time, as has the default setting of

ops$tkyte%ORA11GR2> show parameter skip

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
skip_unusable_indexes                boolean     TRUE


it used to be false by default in 9i.


but the advice still stands as it was written. Inserts will fail (as would updates of the indexed columns and deletes) - raising the error. AND the index will be in the dictionary - it will not have gone 'missing', and the DBA can easily see "oh, wait there are unusable indexes" when diagnosing a performance issue.

So, I stick with setting unusable whenever possible. Better to NOT lose the definition of the index.

Indexes become unusable

Jason, August 22, 2012 - 9:31 am UTC

We have a list partitioned table. On the table there 9 non-partitioned indexes.

Periodically all of the indexes to become unusable.

Any documentation I can find states "Almost any partition-level operation on a table will render any globally partitioned or non-partitioned index unusable.".

Can you confirm that one of the "Almost any partition-level operations" is when we add a partition? Would adding rows via SQL Loader also make them unusable?

Thanks

Thanks
Tom Kyte
August 29, 2012 - 11:23 am UTC

Adding will not

ops$tkyte%ORA11GR2> create table t
  2  ( id     number,
  3    data   varchar2(30)
  4  )
  5  partition by list (id) (
  6      partition p1 values  (1),
  7      partition p2 values  (2),
  8      partition p3 values  (3)
  9  )
 10  ;

Table created.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> insert into t select mod(rownum,3)+1, 'x' from all_users;

38 rows created.

ops$tkyte%ORA11GR2> create index t_idx on t(data) GLOBAL;

Index created.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> alter table t add partition p4 values (4);

Table altered.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> select status from user_indexes where index_name = 'T_IDX';

STATUS
--------
VALID



a direct path load could leave them unusable if they are unique and the uniqueness is violated.