Oracle 索引扫描的五种类型

Oracle索引扫描的五种类型

根据索引的类型与where限制条件的不同,有5种类型的Oracle索引扫描:

(1)索引唯一扫描(indexuniquescan)

2索引范围扫描(indexrangescan)

(3)索引全扫描(indexfullscan)

4索引快速扫描(indexfastfullscan)

(5)索引跳跃扫描(INDEXSKIPSCAN)

.索引唯一扫描(indexuniquescan)

通过唯一索引查找一个数值经常返回单个ROWID。如果该唯一索引有多个列组成(即组合索引),则至少要有组合索引的引导列参与到该查询中,如创建一个索引:createindexidx_testonemp(ename,deptno,loc)。则selectenamefromempwhereename=‘JACK’anddeptno=‘DEV’语句可以使用该索引。如果该语句只返回一行,则存取方法称为索引唯一扫描。而selectenamefromempwheredeptno=‘DEV’语句则不会使用该索引,因为where子句种没有引导列。如果存在UNIQUE或PRIMARYKEY约束(它保证了语句只存取单行)的话,Oracle经常实现唯一性扫描。

如:

SQL>setautottraceonlyexp;--只显示执行计划

SQL>select*fromscott.emptwheret.empno=10;

执行计划

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

Planhashvalue:2949544139

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

|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time

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

|0|SELECTSTATEMENT||1|38|1(0)|00:0

|1|TABLEACCESSBYINDEXROWID|EMP|1|38|1(0)|00:0

|*2|INDEXUNIQUESCAN|PK_EMP|1||0(0)|00:0

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

PredicateInformation(identifiedbyoperationid):

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

2-access("T"."EMPNO"=10)

二.索引范围扫描(indexrangescan)

使用一个索引存取多行数据,同上面一样,如果索引是组合索引,而且selectenamefromempwhereename=JACKanddeptno=DEV’语句返回多行数据,虽然该语句还是使用该组合索引进行查询,可此时的存取方法称为索引范围扫描

在唯一索引上使用索引范围扫描的典型情况下是在谓词(where限制条件)中使用了范围操作符(如>、<、<>、>=、<=、between)

使用索引范围扫描的例子:

SQL>selectempno,enamefromscott.empwhereempno>7876orderbyempno;

执行计划

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

Planhashvalue:169057108

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

|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time

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

|0|SELECTSTATEMENT||1|10|2(0)|00:0

|1|TABLEACCESSBYINDEXROWID|EMP|1|10|2(0)|00:0

|*2|INDEXRANGESCAN|PK_EMP|1||1(0)|00:0

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

PredicateInformation(identifiedbyoperationid):

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

2-access("EMPNO">7876)

在非唯一索引上,谓词可能返回多行数据,所以在非唯一索引上都使用索引范围扫描。

使用indexrangscan3种情况:

(a)在唯一索引列上使用了range操作符(><<>>=<=between)。
(b)在组合索引上,只使用部分列进行查询,导致查询出多行。
(c)对非唯一索引列上进行的任何查询。

三.索引全扫描(indexfullscan)

与全表扫描对应,也有相应的全Oracle索引扫描。在某些情况下,可能进行全Oracle索引扫描而不是范围扫描,需要注意的是全Oracle索引扫描只在CBO模式下才有效。CBO根据统计数值得知进行全Oracle索引扫描比进行全表扫描更有效时,才进行全Oracle索引扫描,而且此时查询出的数据都必须从索引中可以直接得到。

全Oracle索引扫描的例子:

SQL>createindexbig_emponscott.emp(empno,ename);

索引已创建。

SQL>selectempno,enamefromscott.emporderbyempno,ename;

执行计划

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

Planhashvalue:322359667

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

|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|

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

|0|SELECTSTATEMENT||14|140|1(0)|00:00:01|

|1|INDEXFULLSCAN|BIG_EMP|14|140|1(0)|00:00:01|

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

四.索引快速扫描(indexfastfullscan)

扫描索引中的所有的数据块,与indexfullscan很类似,但是一个显著的区别就是它不对查询出的数据进行排序,即数据不是以排序顺序被返回。在这种存取方法中,可以使用多块读功能,也可以使用并行读入,以便获得最大吞吐量与缩短执行时间。

索引快速扫描的例子:

SQL>select/*+index_ffs(daveindex_dave)*/idfromdavewhereid>0;

执行计划

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

Planhashvalue:674200218

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

|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time

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

|0|SELECTSTATEMENT||8|24|2(0)|00:00:0

|*1|INDEXFASTFULLSCAN|INDEX_DAVE|8|24|2(0)|00:00:0

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

PredicateInformation(identifiedbyoperationid):

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

1-filter("ID">0)

为了实现这个效果,折腾了半天,最终还是用hint来了.

OracleHint

http://blog.csdn.net/tianlesoftware/archive/2010/03/05/5347098.aspx

.索引跳跃扫描(INDEXSKIPSCAN

INDEXSKIPSCAN,发生在多个列建立的复合索引上,如果SQL中谓词条件只包含索引中的部分列,并且这些列不是建立索引时的第一列时,就可能发生INDEXSKIPSCAN。这里SKIP的意思是因为查询条件没有第一列或前面几列,被忽略了。

Oracle10g的文档如下:

Indexskipscansimproveindexscansbynonprefixcolumns.Often,scanningindexblocksisfasterthanscanningtabledatablocks.

Skipscanningletsacompositeindexbesplitlogicallyintosmallersubindexes.Inskipscanning,theinitialcolumnofthecompositeindexisnotspecifiedinthequery.Inotherwords,itisskipped.

--skipscan让组合索引(compositeindex)逻辑的split成几个子索引。如果在在查询时,第一个列没有指定,就跳过它。

Thenumberoflogicalsubindexesisdeterminedbythenumberofdistinctvaluesintheinitialcolumn.Skipscanningisadvantageousiftherearefewdistinctvaluesintheleadingcolumnofthecompositeindexandmanydistinctvaluesinthenonleadingkeyoftheindex.

--建议将distinct值小的列作为组合索引的引导列,即第一列。

Example13-5IndexSkipScan

Consider,forexample,atableemployees(sex,employee_id,address)withacompositeindexon(sex,employee_id).Splittingthiscompositeindexwouldresultintwologicalsubindexes,oneforMandoneforF.

Forthisexample,supposeyouhavethefollowingindexdata:

('F',98)

('F',100)

('F',102)

('F',104)

('M',101)

('M',103)

('M',105)

Theindexissplitlogicallyintothefollowingtwosubindexes:

(1)ThefirstsubindexhasthekeyswiththevalueF.

2ThesecondsubindexhasthekeyswiththevalueM.

Figure13-2IndexSkipScanIllustration

http://hi.csdn.net/attachment/201106/3/0_1307083135NKUk.gif

Thecolumnsexisskippedinthefollowingquery:

SELECT*

FROMemployees

WHEREemployee_id=101;

Acompletescanoftheindexisnotperformed,butthesubindexwiththevalueFissearchedfirst,followedbyasearchofthesubindexwiththevalueM.

测试:

创建表:

SQL>createtabledave_testasselectowner,object_id,object_type,createdfromdba_objects;

Tablecreated.

创建组合索引

SQL>createindexidx_dave_test_comondave_test(owner,object_id,object_type);

Indexcreated.

--收集表的统计信息

SQL>execdbms_stats.gather_table_stats('SYS','DAVE_TEST');

PL/SQLproceduresuccessfullycompleted.

SQL>setautottraceonlyexp;

指定组合索引的所有字段时,使用Indexrangescan

SQL>select*fromdave_testwhereowner='SYS'andobject_id=20andobject_type='TABLE';

ExecutionPlan

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

Planhashvalue:418973243

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

|Id|Operation|Name|Rows|Bytes|Cost(

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

|0|SELECTSTATEMENT||1|27|2

|1|TABLEACCESSBYINDEXROWID|DAVE_TEST|1|27|2

|*2|INDEXRANGESCAN|IDX_DAVE_TEST_COM|1||1

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

PredicateInformation(identifiedbyoperationid):

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

2-access("OWNER"='SYS'AND"OBJECT_ID"=20AND"OBJECT_TYPE"='TABLE')

指定组合索引的2个字段时,使用的还是indexrangescan

SQL>select*fromdave_testwhereowner='SYS'andobject_id=20;

ExecutionPlan

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

Planhashvalue:418973243

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

|Id|Operation|Name|Rows|Bytes|Cost(

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

|0|SELECTSTATEMENT||1|27|3

|1|TABLEACCESSBYINDEXROWID|DAVE_TEST|1|27|3

|*2|INDEXRANGESCAN|IDX_DAVE_TEST_COM|1||2

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

PredicateInformation(identifiedbyoperationid):

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

2-access("OWNER"='SYS'AND"OBJECT_ID"=20)

指定组合索引的引导列,即第一个列时,不走索引,走全表扫描

SQL>select*fromdave_testwhereowner='SYS';

ExecutionPlan

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

Planhashvalue:1539627441

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

|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|

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

|0|SELECTSTATEMENT||23567|621K|52(4)|00:00:01|

|*1|TABLEACCESSFULL|DAVE_TEST|23567|621K|52(4)|00:00:01|

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

PredicateInformation(identifiedbyoperationid):

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

1-filter("OWNER"='SYS')

指定组合索引的非引导列,使用Indexskipscan:

SQL>select*fromdave_testwhereobject_id=20andobject_type='TABLE';

ExecutionPlan

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

Planhashvalue:3446962311

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

|Id|Operation|Name|Rows|Bytes|Cost(

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

|0|SELECTSTATEMENT||1|27|22

|1|TABLEACCESSBYINDEXROWID|DAVE_TEST|1|27|22

|*2|INDEXSKIPSCAN|IDX_DAVE_TEST_COM|1||21

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

PredicateInformation(identifiedbyoperationid):

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

2-access("OBJECT_ID"=20AND"OBJECT_TYPE"='TABLE')

filter("OBJECT_ID"=20AND"OBJECT_TYPE"='TABLE')

指定组合索引的最后一列,不走索引,走全表扫描

SQL>select*fromdave_testwhereobject_type='TABLE';

ExecutionPlan

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

Planhashvalue:1539627441

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

|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|

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

|0|SELECTSTATEMENT||1774|47898|52(4)|00:00:01|

|*1|TABLEACCESSFULL|DAVE_TEST|1774|47898|52(4)|00:00:01|

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

PredicateInformation(identifiedbyoperationid):

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

1-filter("OBJECT_TYPE"='TABLE')

指定组合索引的头尾2列,不走索引:

SQL>select*fromdave_testwhereowner='SYS'andobject_type='TABLE';

ExecutionPlan

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

Planhashvalue:1539627441

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

|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|

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

|0|SELECTSTATEMENT||830|22410|52(4)|00:00:01|

|*1|TABLEACCESSFULL|DAVE_TEST|830|22410|52(4)|00:00:01|

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

PredicateInformation(identifiedbyoperationid):

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

1-filter("OBJECT_TYPE"='TABLE'AND"OWNER"='SYS')

通过以上测试,和之前官网的说明,Indexskipscan仅是在组合索引的引导列,即第一列没有指定,并且非引导列指定的情况下。

联合索引选择性更高咯,所占空间应当是比单独索引要少,因为叶节点节省了重复的rowid,当然branch节点可能稍微多一点。

禁用skipscan:

altersystemset_optimizer_skip_scan_enabled=falsescope=spfile;

你可能感兴趣的:(oracle索引扫描类型)