正常情况下,一条SQL语句使用索引,在的where谓语条件中要出索引的左边部分(where条件出现字段从建索引的字段的顺序左边字段开始,例如:create index ind on table(column1,column2,column3),只有where条件出现了下列谓语:column1、column1,column2、column1,column2,column3;才会使用索引。

创建表T
create table t as select decode(mod(rownum,2),0,'F',2,'M') flag,t.* from all_objects t;
 commit;创建索引
 create index ind_t on t(flag,object_id);

 获取统计数据
 analyze table t compute statistics;
 analyze index ind_t compute statistics; 

情况一:快速全面扫描索引:因为需要查询的和where语句所关联的字段都在索引中,并且索引一般比表小得多。从而减少物理IO,提高查询性能,
 SQL> set autot traceonly exp
 SQL> select flag,object_id from t;
 执行计划
 ----------------------------------------------------------
 Plan hash value: 1148684643
 ------------------------------------------------------------------------------
 | Id  | Operation            | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
 ------------------------------------------------------------------------------
 |   0 | SELECT STATEMENT     |       | 70636 |   344K|    48   (0)| 00:00:01 |
 |   1 |  INDEX FAST FULL SCAN| IND_T | 70636 |   344K|    48   (0)| 00:00:01 |
 ------------------------------------------------------------------------------
 SQL>情况二:索引跳跃式扫描:如果索引的左边键值非常基数非常少,优化器会使用这种方式检索。
SQL> select flag,object_id from t where object_id=1231;
 执行计划
 ----------------------------------------------------------
 Plan hash value: 3688940926
 --------------------------------------------------------------------------
 | Id  | Operation        | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
 --------------------------------------------------------------------------
 |   0 | SELECT STATEMENT |       |     1 |     5 |     2   (0)| 00:00:01 |
 |*  1 |  INDEX SKIP SCAN | IND_T |     1 |     5 |     2   (0)| 00:00:01 |
 --------------------------------------------------------------------------
 Predicate Information (identified by operation id):
 ---------------------------------------------------
    1 - access("OBJECT_ID"=1231)
        filter("OBJECT_ID"=1231)SQL> select * from t where object_id=100;
  
  select * from t where object_id<100;这个使用跳越索引select * from t where object_id>100;这个走全表扫描
索引是否使用索引 oracle会根据获取数据块数占总行数大小(对于有一些表大于多块读的块个数的表)
修改索引左边的字段,让他的基数更高
alter table t modify flag varchar2(10);
update t set flag=to_char(mod(rownum,10000)) ;
收集统计信息
 exec dbms_stats.gather_table_stats(user,'T',cascade=>true);
select * from t where object_id=1231;  这样就可能不走跳跃索引

情况三:行数统计
select count(*) from t
可能不会考虑直接遍历索引,因为在B树索引中,存在null。

情况四:索引列上使用函数
select * from t where fun(index_columns)=223;
因为索引列上使用的函数,索引不能直接使用索引列直接创建的索引,应该使用这个函数所对应的函数索引,并且这个函数是deterministic类性
情况五:索引列显示或者隐式转换
select * from t where  index_column=5;
如果index_column是一个字符类型,这样相当如oracle调用了to_number(index_column)=5,从而无法使用索引
 SQL> explain plan for select * from t where flag=3;
 已解释。
 SQL> select * from table(dbms_xplan.display);
 PLAN_TABLE_OUTPUT
 --------------------------------------------------------------------------------
 Plan hash value: 1601196873


 --------------------------------------------------------------------------
 | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
 --------------------------------------------------------------------------
 |   0 | SELECT STATEMENT  |      |   495 | 51975 |  2075   (1)| 00:00:25 |
 |*  1 |  TABLE ACCESS FULL| T    |   495 | 51975 |  2075   (1)| 00:00:25 |
 --------------------------------------------------------------------------
 Predicate Information (identified by operation id):
 ---------------------------------------------------
 PLAN_TABLE_OUTPUT
 --------------------------------------------------------------------------------
    1 - filter(TO_NUMBER("FLAG")=3)

 已选择13行。SQL> explain plan for select /*+ index(t ind_t)*/ * from t where flag=3;
 已解释。
 SQL> select * from table(dbms_xplan.display);
 PLAN_TABLE_OUTPUT
 --------------------------------------------------------------------------------
 Plan hash value: 1099373541
 --------------------------------------------------------------------------------
 | Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time|--------------------------------------------------------------------------------
 PLAN_TABLE_OUTPUT
 --------------------------------------------------------------------------------
 |   0 | SELECT STATEMENT            |       |   495 | 51975 |  2491   (1)| 00:00:30 |
 |   1 |  TABLE ACCESS BY INDEX ROWID| T     |   495 | 51975 |  2491   (1)| 00:00:30 |
 |*  2 |   INDEX FULL SCAN           | IND_T |   495 |       |  1998   (1)| 00:00:24 |
 --------------------------------------------------------------------------------
 PLAN_TABLE_OUTPUT
 --------------------------------------------------------------------------------
 Predicate Information (identified by operation id):
 ---------------------------------------------------
    2 - filter(TO_NUMBER("FLAG")=3)
 已选择14行。
SQL> explain plan for select * from t where flag='3';
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time
 |   0 | SELECT STATEMENT            |       |   495 | 51975 |   497   (0)| 00:00:06 |
 |   1 |  TABLE ACCESS BY INDEX ROWID| T     |   495 | 51975 |   497   (0)| 00:00:06 |
 |*  2 |   INDEX RANGE SCAN          | IND_T |   495 |       |     4   (0)| 00:00:01 |

在where谓语条件中,减少对数据列使用函数

情况六:根据统计表的大小和访问数据量来决定是否使用索引

根据统计信息,oracle会假设数据分布均匀,获取的信息不仅仅包含索引。

1、如果获取的数据占总行数的非常小比例,并且这张表非常大,这时会使用索引。

2、如果获取的数据占总行数的非常大比例,或者数据表非常小,这时会使用全表扫描。