
Suppose you want to find out which type of index is best for performance with your workload. Why not set up a competition and let the optimizer decide? The playground:
ADAM@pdb1 > select max(amount_sold) from sales where channel_id=9;
MAX(AMOUNT_SOLD)
----------------
5000
ADAM@pdb1 > @lastplan
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------
SQL_ID 3hrvrf1r6kn8s, child number 0
-------------------------------------
select max(amount_sold) from sales where channel_id=9
Plan hash value: 3593230073
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 4 (100)| |
| 1 | SORT AGGREGATE | | 1 | 6 | | |
| 2 | TABLE ACCESS BY INDEX ROWID BATCHED| SALES | 1 | 6 | 4 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | BSTAR | 1 | | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("CHANNEL_ID"=9)
20 rows selected.
There is a standard B*tree index on the column CHANNEL_ID that speeds up the SELECT above. I think a bitmap index would be better:
ADAM@pdb1 > create bitmap index bmap on sales(channel_id) invisible nologging;
Index created.
ADAM@pdb1 > alter index bstar invisible;
Index altered.
ADAM@pdb1 > alter index bmap visible;
Index altered.
ADAM@pdb1 > select max(amount_sold) from sales where channel_id=9;
MAX(AMOUNT_SOLD)
----------------
5000
ADAM@pdb1 > @lastplan
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------
select max(amount_sold) from sales where channel_id=9
Plan hash value: 2178022915
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 3 (100)| |
| 1 | SORT AGGREGATE | | 1 | 6 | | |
| 2 | TABLE ACCESS BY INDEX ROWID BATCHED| SALES | 1 | 6 | 3 (0)| 00:00:01 |
| 3 | BITMAP CONVERSION TO ROWIDS | | | | | |
|* 4 | BITMAP INDEX SINGLE VALUE | BMAP | | | | |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("CHANNEL_ID"=9)
21 rows selected.
With this 12c New Feature (two indexes on the same column), I got a smooth transition to the new index type. But this left no choice to the optimizer. What about this?
ADAM@pdb1 > alter index bmap invisible; Index altered. ADAM@pdb1 > alter session set optimizer_use_invisible_indexes=true;
Now both indexes are invisible and the optimizer may choose any of them. Turns out that it likes the bitmap index better here. Instead of watching the execution plans, V$SEGMENT_STATISTICS can also be used to find out:
ADAM@pdb1 > select object_name,statistic_name,value
from v$segment_statistics
where object_name in ('BSTAR','BMAP')
and statistic_name in ('physical reads','logical reads');
OBJECT STATISTIC_NAME VALUE
------ ------------------------------ ----------
BSTAR logical reads 22800
BSTAR physical reads 6212
BMAP logical reads 1696
BMAP physical reads 0
The numbers of BSTAR remain static while BMAP numbers increase. You may also monitor that with DBA_HIST_SEG_STAT across AWR snapshots. Now isn’t that cool? 🙂
Couple of things to be aware of here:
Watch out for more than just physical/logical reads – bitmap indexes may cause a locking problem in an OLTP environment.
Don’t keep the two indexes invisible forever – after you saw which one performs better, drop the other one. Invisible indexes need to be maintained upon DML and therefore slow it down.

#1 von ferny am August 9, 2016 - 17:43
what commands are in „lastplan.sql“ script?
#2 von Uwe Hesse am August 11, 2016 - 09:24
lastplan.sql does this: select plan_table_output from table(dbms_xplan.display_cursor); That shows the execution plan of the last SQL in the session, taken from the library cache.
#3 von Betty Zheng am September 26, 2016 - 19:22
wow very nice try, thanks for posting!
#4 von dba am September 27, 2016 - 16:15
when creating the bitmap index on the same column, I got ORA-01408: such column list already indexed. Is there any setting I have to turn on? 12.1.0.2 is my version.
#5 von Uwe Hesse am September 27, 2016 - 16:47
@Betty, thank you for leaving a nice feedback, I appreciate it 🙂
#6 von Uwe Hesse am September 27, 2016 - 16:48
@dba, maybe you forgot to create the index invisible? Also, the index type of the new index must be different from the existing index.
#7 von Sam am September 27, 2016 - 19:33
Thank you, its really helpfull.