正常情況下,一條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、如果獲取的數據佔總行數的非常大比例,或者數據表非常小,這時會使用全表掃描。