Showing posts with label index. Show all posts
Showing posts with label index. Show all posts

Sunday, November 21, 2010

ORA-00959: tablespace '_$deleted$16$0' does not exist

Found error
ORA-00959: tablespace '_$deleted$16$0' does not exist
During add partitioned table: ALTER TABLE ... ADD PARTITION...UPDATE INDEXES

Then checked Oracle Support and fixed issue.
ORA-00959: Tablespace '_$deleted$11$0' Does Not Exist - During DML/DDL On Partitoned Table [ID 1121059.1]
The default tablespace attribute of the table is set to a system generated name that represents a dropped tablespace. (I don't explain... we can read on Oracle Support, that is useful)

On oracle support show idea with partitioned table, we can check on dba_part_tables view
select owner, table_name,def_tablespace_name from dba_part_tables where def_tablespace_name like '%delete%';
and solve by -
alter table table_name modify default attributes tablespace tablespace_existing;
but my issue, that is about partitioned index:
SQL> select owner, index_name,def_tablespace_name from dba_part_indexes where def_tablespace_name like '%delete%';

OWNER TABLE_NAME DEF_TABLESPACE_NAME
------------------------------ ------------------------------ ------------------------------
USER TEST_IND01 _$deleted$16$0
USER TEST_IND02 _$deleted$16$0
SQL> alter index USER.TEST_IND01 modify default attributes tablespace TBS_EXISTING ;

Index altered.

SQL> alter index USER.TEST_IND02 modify default attributes tablespace TBS_EXISTING ;

Index altered.
and then add partitioned table again(no error).

Reference:
Oracle Support

Monday, May 10, 2010

ORDER BY to use Index

It's difficult thing, If we try to learn something every days, every days. However It's a good thing, If we love and enjoy to learn.

So I planed to learn something (new) every days. I hope so.
I learned... (It's not new... just something we might forgot).

If a user requires the optimizer to consider using an index to drive an ORDER BY statement instead of sorting, then the indexed columns must be NOT NULL as otherwise they are not considered.
SQL> create table a (x VARCHAR2(128), y VARCHAR2(19));

Table created.

SQL> desc A
Name Null? Type
----------------------------------------------------------------------- -------- ------------------------------------------------
X VARCHAR2(128)
Y VARCHAR2(19)

SQL> insert into a select OBJECT_NAME, OBJECT_TYPE from user_objects;

43 rows created.

SQL> commit;

Commit complete.

SQL> create index a_indx1 on a (x,y);

Index created.

SQL> exec dbms_stats.gather_index_stats(USER,'A_INDX1');

PL/SQL procedure successfully completed.

SQL> explain plan for select * from a order by x,y;

Explained.

SQL> select * from TABLE(dbms_xplan.display(NULL, NULL , 'ALL'));

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 3819873049
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 43 | 3311 | 4 (25)| 00:00:01 |
| 1 | SORT ORDER BY | | 43 | 3311 | 4 (25)| 00:00:01 |
| 2 | TABLE ACCESS FULL| A | 43 | 3311 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------
Indexes Columns be NULL, then change to be Not NULL.
SQL> alter table a modify (x not null, y not null);

Table altered.

SQL> desc A
Name Null? Type
----------------------------------------------------------------------- -------- ------------------------------------------------
X NOT NULL VARCHAR2(128)
Y NOT NULL VARCHAR2(19)

SQL> explain plan for select * from a order by x,y;

Explained.

SQL> select * from TABLE(dbms_xplan.display(NULL, NULL , 'ALL'));

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 2241847120
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 43 | 3311 | 1 (0)| 00:00:01 |
| 1 | INDEX FULL SCAN | A_INDX1 | 43 | 3311 | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------
After changed to be Not NULL, the optimizer consider an index.

Sunday, November 01, 2009

Learn "NOLOGGING" from forums

I often learn something(oracle) and find idea from OTN Forums.

And This Topic(Wait Events "log file parallel write" / "log file sync" during CREATE INDEX), It wrote about nologging option, “log file sync” and “log file parallel write” waits, and it's replied by Jonathan Lewis and Uwe Hesse.

Jonathan Lewis noted 2 points with "create index" nologging option:
it's "only" an index, so you could always rebuild it in the event of media corruption, but if you had lots of indexes created nologging this might cause an unreasonable delay before the system was usable again - so you should decide on a fallback option, such as taking a new backup of the tablespace as soon as all the nologging operatons had completed.

If the database, or that tablespace, is in "force logging" mode, the nologging will not work.

Sunday, August 31, 2008

ORA-08102: index key not found, obj# ... Solution to resolve

When updated table, found error:

ORA-08102: index key not found, obj# 116528, file 96, block 8795 (2)

This problem I had resolved by rebuild indexes (ORA-08102 on index objects). But today I can not use this solution:

What is about this error?

Error: ORA-08102

Oracle10g Message
Text: index key not found, obj# %s, file %s, block %s (%s)
Oracle 9.2 or Earlier Error Message
Text: index key not found, obj# %s, dba %s (%s)

ORA-08102 is a mismatch between the key(s) stored in the index and the values stored in the table.

What typically happens is the index is built and at some future time, some type of corruption occurs, either in the table or index, to cause the mismatch.

When know about is, what is the way to resolve it? How?

-> Check object, that have the problem, if it's index... try to rebuild:

SQL>select object_name, object_type
from dba_objects
where object_id = [obj# in ORA-08102]

Example:

SQL> select object_name, object_type
from dba_objects
where object_id = 116528;

OBJECT_NAME OBJECT_TYPE
--------------- -------------------
XXX_IDX07 INDEX PARTITION

And then rebuild it.

SQL> alter index [index_name] rebuild [online];

OR "partition index"

SQL> select partition_name from dba_segments where TABLESPACE_NAME = (select tablespace_name from dba_data_files where file_id=[file_id from ORA-08102] ) and SEGMENT_NAME=[object_name from dba_objects];

SQL> select partition_name from dba_segments where TABLESPACE_NAME = (select tablespace_name from dba_data_files where file_id=96) and SEGMENT_NAME='XXX_IDX07';

PARTITION_NAME
------------------------------
XXX_PART1


SQL> alter index [index_name] rebuild partition [partition_name] [online];

if ORA-08102 on table objects -> resolve by "analyze table ... validate structure.. or use dbv to check corrupt on table" ) don't forget check error on OS , trace file and alert log file.

-> (ORA-08102 on index objects), if rebuilt indexes, But still error ORA-08102: Drop that index and recreate it.


Monday, June 23, 2008

Create Index: ORA-01450: maximum key length (6398) exceeded

Today ,I think about the key length to create index on Oracle.

So I look for the information on Internet.

Error: ORA 1450

Text: maximum key length exceeded
-------------------------------------------------------------------------------

Cause: The combined length of all the columns specified in a CREATE INDEX statement exceeded the maximum index length.
The maximum index length varies by operating system. The total index length is computed as the sum of the width of all indexed columns plus the number of indexed columns.
Date fields have a length of 7, character fields have their defined length, and numeric fields have a length of 22.Numeric length = (precision/2) + 1. If negative, add +1.

Action: Select columns to be indexed so the total index length does not exceed
the maximum index length for the operating system.
See also your operating system-specific Oracle documentation.


Oracle Reference =>

>>>

How it is Calculated
------------------------------------------------------
When creating an Index, the total length of the index cannot exceed a certain value. This value depends primarily on the DB_BLOCK_SIZE. If an attempt is made to create an index larger than the Maximum value, an ORA-1450 is raised:

ORA-01450 maximum key length (758) exceeded ->(2K Block)
ORA-01450 maximum key length (1578) exceeded ->(4K block)
ORA-01450 maximum key length (3218) exceeded ->(8K Block)
ORA-01450 maximum key length (6498) exceeded ->(16K Block)

The number in parends is the maximum allowable length of the index key for that particular system.

So, how is this number calculated?

The maximum key size means:

The total index length + length of the key (2 Bytes) + ROWID (6 Bytes) + the length of the rowid (1 byte).

The total index length is computed as the sum of the width of all indexed columns plus the number of indexed columns. Date fields have a length of 7, character fields have their defined length, and numeric fields have a length of 22. Numeric length = (precision/2) + 1. If negative, add +1.

For Funtion-based indexes, we must calculate the length of the return type.

This index key size is limited by the value of db_block_size, because a key value may not span multiple blocks. In fact, it is required that any index block must contain at least TWO index entries per block.

Therefore, the maximum key length for an index will be less than half of the DB_BLOCK_SIZE. The Oracle 8i Administrator's Guide states that the maximum size of a single index entry is approximately one-half the data block size. However, when considering that we must also leave space in the block according to PCTFREE, INITRANS, and space for block overhead (Block Header, ROW Directory, Table Directory, etc) the actual space that can be used for the Index key is actually just over 1/3 of the DB_BLOCK_SIZE.


Using default values for these storage options, the maximum length for indexes is as follows for different block sizes:

DB_BLOCK_SIZE: Maximum Index Key Length:
============== =========================

2K (2048) 758 Bytes
4K (4096) 1578 Bytes
8K (8192) 3218 Bytes
16K (16384) 6498 Bytes


If you hit a maximum key length in an index according to the DB_BLOCK_SIZE, you may need to recreate the database with a larger block size. The other alternative is to limit the size of the index. This is slightly more difficult with a Function-based index, when the return type is a varchar or RAW.

To limit the size of a function-based index you should consider using the SUBSTR or SUBSTRB function, to limit the number of Characters or Bytes returned. For more information on SUBTR and SUBSTRB, refer to the Oracle8i SQL Reference Guide.

>>>

NOTE: In Oracle9i or above, the maximum key length is larger

Example: on Oracle 11G

SQL> create table temp01 (data1 varchar2(4000), data2 varchar2(4000));

Table created.

SQL>
SQL>
SQL> create index idx_temp01 on temp01(data1,data2);

create index idx_temp01 on temp01(data1,data2)
*
ERROR at line 1:
ORA-01450: maximum key length (6398) exceeded

SQL> show parameter db_block_size;

NAME TYPE VALUE

------------------------------------ ----------- -------

db_block_size integer 8192


Enjoy...

Tuesday, January 22, 2008

Any Idea About "INITRANS"

What is "INITRANS"?
Specify the initial number of concurrent transaction entries allocated within each data block allocated to the database object. This value can range from 1 to 255 and defaults to 1, with the following exceptions: (on10g MAX_TRANS 's ignored )

* The default INITRANS value for a cluster is 2 or the default INITRANS value of the tablespace in which the cluster resides, whichever is greater.

* The default value for an index is 2.

In general, you should not change the INITRANS value from its default.

Each transaction that updates a block requires a transaction entry in the block. The size of a transaction entry depends on your operating system. This parameter ensures that a minimum number of concurrent transactions can update the block and helps avoid the overhead of dynamically allocating a transaction entry.

If you have global indexes, a global index segment and global index blocks are shared by server processes of the same parallel DML statement. Even if the operations are not performed against the same row, the server processes can share the same index blocks. Each server transaction needs one transaction entry in the index block header before it can make changes to a block.

Therefore, in the CREATE INDEX or ALTER INDEX statements,

you should set INITRANS, the initial number of transactions allocated within each data block, to a large value, such as the maximum DOP against this index

BLOCK SIZE and INITRANS :
=========================

Each ITL entry in the block transaction variable header takes 24 bytes. Though a block can have a maximum of 255 different ITLs, the ITLs allocation is limited by block size. The database block size plays an important role when allocating the number of inital ITLs for the block. The rule is "the total size allocated for initial ITLs SHOULD be LESS THAN 50% of the database block size".

ie : sizeof(INITIAL ITLs) < ( 50 % of the DATABASE BLOCK SIZE )

Examining ITL allocation (a brief experiment)
==========================

STEP 1 : Create a table with INITRANS 10.
SQL> CREATE TABLE TEST (I NUMBER) INITRANS 10;

STEP 2: Insert 1 record for testing purpose. A blockdump can be taken later.
SQL> INSERT INTO TEST VALUES (10);
SQL> COMMIT;

Step 3: Find out the block number and the file id for dumping the block:
SQL> select dbms_rowid.rowid_relative_fno(rowid), dbms_rowid.rowid_block_number(rowid) from test;

Step 4: Dump the block:
SQL> ALTER SYSTEM DUMP DATAFILE x BLOCK y;

Step 5: Open the dump trace file located in USER_DUMP_DEST directory and check the following:

Block header dump: 0x0040ad12
Object id on Block? Y
seg/obj: 0x5881 csc: 0x5dc.33121987 itc: 10 flg: O typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01

Itl Xid Uba Flag Lck Scn/Fsc
0x01 xid: 0x0003.05b.0000009b uba: 0x008005f3.005e.42 --U- 1 fsc 0x0000.33121989
0x02 xid: 0x0000.000.00000000 uba: 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x03 xid: 0x0000.000.00000000 uba: 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x04 xid: 0x0000.000.00000000 uba: 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x05 xid: 0x0000.000.00000000 uba: 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x06 xid: 0x0000.000.00000000 uba: 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x07 xid: 0x0000.000.00000000 uba: 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x08 xid: 0x0000.000.00000000 uba: 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x09 xid: 0x0000.000.00000000 uba: 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x0a xid: 0x0000.000.00000000 uba: 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000

Example:
SQL> CREATE TABLE TEST (I NUMBER) INITRANS 10;

Table created.

SQL> INSERT INTO TEST VALUES (10);

1 row created.

SQL> commit;

Commit complete.

SQL> select dbms_rowid.rowid_relative_fno(rowid), dbms_rowid.rowid_block_number(rowid) from test;

DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
------------------------------------------------------------------------
4 36477

SQL> ALTER SYSTEM DUMP DATAFILE 4 block 36477;

System altered.

Check tracefile:

Block header dump: 0x01008e7d
Object id on Block? Y
seg/obj: 0x18fec csc: 0x00.278431ed itc: 10 flg: E typ: 1 - DATA
brn: 0 bdba: 0x1008e79 ver: 0x01 opc: 0
inc: 0 exflg: 0

Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0008.019.0017e02b 0x01800ce1.17d9.08 --U- 1 fsc 0x0000.278431ef
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x03 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x04 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x05 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x06 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x07 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x08 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x09 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x0a 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
The above dump confirms that the ITL allocation worked as specified. However this may not be the case for all the valid values for INITRANS.

Assuming that your database has 2k block size (db_block_size=2048), if you set INITRANS to 45, only 41 ITLs will be found in the dump because of the consideration of the block size. So for a 2k block size, any value greater than 41 for INITRANS would result only in allocating 41 ITL entries in the block header.

Likewise , if you have a 4k block size, the maximum number of inital ITL entries are only 83. Any value over and above 83 for INITRANS are ignored.

BLOCK SIZE NO OF ITLs allocated in block header
=============================================

2048 41
4096 83
8192 169

As I mentioned before, not more than 50 % of the block is utilized for ITLs. For a 2k block sized database, the maximum ITLs during intial allocation is 41. Each ITL takes 24 bytes of space in the header. So

For 2k Block: 41 * 24 = 984 bytes. This is 48 % of the total block size.

For 4k block: 83 * 24 = 1992 bytes.

For 8k block: 169 * 24 = 4056 bytes. (Same 48%)

Follow the above 5 steps to check the ITL allocation for different block sizes.
==================================================================================

We can modify INITRANS attribute by use ALTER

EXAMPLE
SQL> ALTER TABLE TABLE_NAME INITRANS 10;

Example: Test on 8k ... initrans =255
SQL> CREATE TABLE TEST2 (I NUMBER) INITRANS 255;

Table created.

SQL> INSERT INTO TEST2 VALUES (10);

1 row created.

SQL> commit;

Commit complete.

SQL> select dbms_rowid.rowid_relative_fno(rowid), dbms_rowid.rowid_block_number(rowid) from test2;

DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
------------------------------------ ------------------------------------
4 36485

SQL> ALTER SYSTEM DUMP DATAFILE 4 block 36485;

System altered.

Check trace file:

Block header dump: 0x01008e85
Object id on Block? Y
seg/obj: 0x18fed csc: 0x00.2784367f itc: 169 flg: E typ: 1 - DATA
brn: 0 bdba: 0x1008e81 ver: 0x01 opc: 0
inc: 0 exflg: 0

Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0004.008.0011e01c 0x01800c72.154d.05 --U- 1 fsc 0x0000.27843681
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
.
.
.
0xa5 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0xa6 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0xa7 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0xa8 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000


So, use shell command to help

$ grep fsc oradb1_ora_15394.trc | wc -l
169

So,
8k block: 169 * 24 = 4056 bytes. (Same 48%)... not 255

You should know before modify =>

Cautions on Altering Tables Physical Attributes The values you specify in this clause affect the table as follows:

* For a nonpartitioned table, the values you specify override any values specified for the table at create time.

* For a range-, list-, or hash-partitioned table, the values you specify are the default values for the table and the actual values for every existing partition, overriding any values already set for the partitions. To change default table attributes without overriding existing partition values, use the modify_table_default_attrs clause.

* For a composite-partitioned table, the values you specify are the default values for the table and all partitions of the table and the actual values for all subpartitions of the table, overriding any values already set for the subpartitions. To change default partition attributes without overriding existing subpartition values, use the modify_table_default_attrs clause with the FOR PARTITION clause.

---------------------------------------

When will I rebuild Index?

That's a question in myself with Oracle JOB. Actually I have known a long time.
But I confused and asked myself. So I posted on Oracle Forum.
I have seen many people to answer my question.

I should say "Thanks" to "Donald K Burleson" for idea and reference.
http://www.dba-oracle.com/t_index_rebuilding_issues.htm

# Index rebuilds can improve SQL performance - On indexes with heavy delete activity, rebuilding has been proven to improve SQL performance for range queries. Andrew Kerber notes "our observation was that on 20 million row tables and upward, as little as 5% deletes, regardless of other factors, would cause sufficient performance degradation to justify an index rebuild."

# Index rebuilds can be safely automated - For shops that are concerned about space reclamation, batch jobs can be implemented to reliably rebuild all indexes automatically. I've personally used crontab jobs for index rebuilding when my client's insisted on scheduled index rebuilding, and there is a very low risk of causing a production problem, provided that the rebuild are performed during regularly scheduled maintenance windows using Oracle Best Practices.

# Index rebuilds can release free space - It's well documented that index rebuilding/coalesce will release lost space (see the Oracle Segment Advisor) and in "rare cases", an index rebuild may cause a performance boost, but only under certain conditions.

# Blocksize matters - It's generally accepted that a larger blocksize results in faster full scans.

Anyway I sure it improve performance when I rebuild indexes.
Let me back in my question... haha
Idea about rebuild index... I can see in metalink (Note:77574.1)

Rebuild the index when these conditions are true:
- deleted entries represent 20% or more of the current entries.
- the index depth is more then 4 levels.

Guidelines on When to Rebuild an Index:
=======================================
You have to periodically check your indexes to see if they become skewed and,
therefore, good candidates for rebuild.

A skewed index has many records clumped close together on the index tree due to their similar indexed values. When an index is skewed, parts of an index are accessed more frequently than others. As a result, disk contention may occur, creating a bottleneck in performance. It is important to periodically examine your indexes to determine if they have become skewed and might need to be rebuilt.

Here is a sample procedure on how to identify the skewed indexes:

1. Gather statistics on your indexes. For large indexes (over one hundred thousand records in the underlying table), use ESTIMATE instead of COMPUTE STATISTICS.
For example:
SQL> analyze index A1_PK compute statistics;
Index analyzed.

2. Run the script given below - index_check.sql - to find out how skewed each index is.
This query checks on all indexes that belong to user SCOTT:
SQL> select index_name, blevel,
decode(blevel,0,'OK BLEVEL',1,'OK BLEVEL',
2,'OK BLEVEL',3,'OK BLEVEL',4,'OK BLEVEL','BLEVEL HIGH') OK
from dba_indexes
where owner='SCOTT';
INDEX_NAME BLEVEL OK
---------------------------------------- ------ ----

A1_PK BLEVEL HIGH
A1_UK BLEVEL HIGH
BUDVERPORT_BUDVERPORT2_UK 1 OK BLEVEL
BUDVERPORT_BV_FK_I 2 OK BLEVEL
BUDVERPORT_CHAR_CL_FK_I 1 OK BLEVEL
BUDVERPORT_DIRCTE_FK_I 3 OK BLEVEL
BUDVERPORT_FUND_TYPE_FK_I 1 OK BLEVEL
BUDVERPORT_OMB_SUBFCT_FK_I 1 OK BLEVEL
BUDVERPORT_ORG_FK_I 0 OK BLEVEL
BUDVERPORT_PL_TITLE_FK_I 1 OK BLEVEL
BUDVERPORT_RDC_FK_I 1 OK BLEVEL
S_INVENTORY_PRODID_WARID_PK BLEVEL HIGH
S_ITEM_ORDID_ITEMID_PK BLEVEL HIGH
S_ITEM_ORDID_PRODID_UK BLEVEL HIGH
S_LONGTEXT_ID_PK BLEVEL HIGH
S_ORD_ID_PK BLEVEL HIGH
S_PRODUCT_ID_PK BLEVEL HIGH
S_PRODUCT_NAME_UK BLEVEL HIGH
S_REGION_ID_PK BLEVEL HIGH
S_REGION_NAME_UK BLEVEL HIGH
S_TITLE_TITLE_PK BLEVEL HIGH
S_WAREHOUSE_ID_PK BLEVEL HIGH

3. The BLEVEL (or branch level) is part of the B-tree index format and relates to the number of times Oracle has to narrow its search on the index while searching for a articular record. In some cases, a separate disk hit is requested for each BLEVEL. Prior to 9i, if the BLEVEL is more than 4, it is recommended to rebuild the index. As database are getting bigger and bigger, BLEVEL may not be a good indicator of time to rebuild the index. BLEVEL > 4 may suggest an evaluation of whether the partitioning option could help you.

Note: If you do not analyze the index, the index_check.sql script will
show "BLEVEL HIGH" for such an index.


4. Gather more index statistics using the VALIDATE STRUCTURE option of the ANALYZE command to populate the INDEX_STATS virtual table. This table does not contain an OWNER column and assumes you are looking for statistics for indexes created by your active session only.
SQL> analyze index SCOTT.ORG_PK validate structure;
Index analyzed.
SQL> select DEL_LF_ROWS*100/decode(LF_ROWS, 0, 1, LF_ROWS) PCT_DELETED,
(LF_ROWS-DISTINCT_KEYS)*100/ decode(LF_ROWS,0,1,LF_ROWS) DISTINCTIVENESS
from index_stats
where NAME='&index_name';
Enter value for index_name: ORG_PK

PCT_DELETED DISTINCTIVENESS
----------- ---------------
0 0

The PCT_DELETED column shows what percent of leaf entries (index entries) have been deleted and remain unfilled. The more deleted entries exist on an index, the more unbalanced the index becomes. If the PCT_DELETED is 20% or higher, the index is candidate for rebuilding. If you can afford to rebuild indexes more frequently, then do so if the value is higher than 10%. Leaving indexes with high PCT_DELETED without rebuild might cause excessive redo allocation on some systems.

The DISTINCTIVENESS column shows how often a value for the column(s) of the index is repeated on average. For example, if a table has 10000 records and 9000 distinct SSN values, the formula would result in 10000-9000) x 100 / 10000 = 10. This shows a good distribution of values.

If, however, the table has 10000 records and only 2 distinct SSN values, the formula would result in (10000-2) x 100 /10000 = 99.98. This shows that there are very few distinct values as a percentage of total records in the column. Such columns are not candidates for a rebuild but good candidates for bitmapped indexes.