ITPub博客

首页 > 数据库 > Oracle > 【Oracle】 索引的扫描方式

【Oracle】 索引的扫描方式

Oracle 作者:dmcatding 时间:2018-07-31 17:13:57 0 删除 编辑

1、TABLE ACCESS BY INDEX ROWID表示回表,单块读;

2、INDEX UNIQUE SCAN 表示索引唯一扫描,单块读;

对唯一索引或者对主键列进行等值查找,就会走INDEX UNIQUE SCAN,因为对唯一索引或者对主键列进行等值查找,CBO能确保最多只返回1行数据,所以这时可以走索引唯一扫描。

INDEX UNIQUE SCAN最多只返回一行数据,只会扫描“索引高度”个索引块,在所有的Oracle访问路径中,其性能仅次于TABLE ACCESS BY USER ROWID。

SQL&get; select * from emp where empno=7369;


Execution Plan

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

Plan hash value: 2949544139


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

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

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

|   0 | SELECT STATEMENT            |        |     1 |    38 |     1   (0)| 00:00:01 |

|   1 |  TABLE ACCESS BY INDEX ROWID| EMP    |     1 |    38 |     1   (0)| 00:00:01 |

|*  2 |   INDEX UNIQUE SCAN         | PK_EMP |     1 |       |     0   (0)| 00:00:01 |

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

Predicate Information (identified by operation id):

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

   2 - access("EMPNO"=7369)

    因为empno是主键列,对empno进行等值访问,就走了INDEX UNIQUE SCAN

    

3、INDEX RANGE SCAN表示索引范围扫描,单块读,返回的数据是有序的(默认升序)。HINT: INDEX(表名/别名 索引名)。对唯一索引或者主键进行范围查找,对非唯一索引进行等值查找,范围查找,就会发生INDEX RANGE SCAN。等待事件为db file sequential read。

SQL&get; select * from test where object_id=100;

Execution Plan

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

Plan hash value: 3946039639

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

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

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

|   0 | SELECT STATEMENT            |        |     1 |    97 |     2   (0)| 00:00:01 |

|   1 |  TABLE ACCESS BY INDEX ROWID| TEST   |     1 |    97 |     2   (0)| 00:00:01 |

|*  2 |   INDEX RANGE SCAN          | IDX_ID |     1 |       |     1   (0)| 00:00:01 |

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

Predicate Information (identified by operation id):

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

   2 - access("OBJECT_ID"=100)


索引范围扫描默认是从索引中最左边的叶子块开始,然后往右边的叶子块扫描(从小到大),当检查到不匹配的数据的时候,就停止扫描。现在将过滤条件改为小于,并且对过滤列进行降序排序,

    

查看执行计划:

SQL&get; select * from test where object_id<100 order by object_id desc;

98 rows selected.

Execution Plan

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

Plan hash value: 1069979465


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

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

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

|   0 | SELECT STATEMENT             |        |    96 |  9312 |     4   (0)| 00:00:01 |

|   1 |  TABLE ACCESS BY INDEX ROWID | TEST   |    96 |  9312 |     4   (0)| 00:00:01 |

|*  2 |   INDEX RANGE SCAN DESCENDING| IDX_ID |    96 |       |     2   (0)| 00:00:01 |

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


Predicate Information (identified by operation id):

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


   2 - access("OBJECT_ID"<100)

       filter("OBJECT_ID"<100)


INDEX RANGE SCAN DECENDING表示索引降序范围扫描,从右往左扫描,返回的数据是降序显示的。

结论:

假设一个索引叶子块能存储100行数据,通过索引返回100行以内的数据只扫描“索引高度”这么多个索引块,如果通过索引返回200行数据,就需要扫描2个叶子块,通过索引返回的行数越多,

扫描的索引叶子块也就越多,随着扫描的叶子块个数的增加,索引范围扫描的性能开销也就越大。

如果索引范围扫描需要回表,同样假设一个索引叶子块能存储100行数据,如果通过索引返回1000行数据,

只需要扫描10个索引叶子块(10个单块读),但是回表可能会需要访问几十个到几百个表块(几十到几百个单块读)。

在检查执行计划的时候要注意索引范围扫描返回多少行数据,如果返回少量数据,不会出现性能问题,如果返回大量数据,在没有回表的情况下也还好,

如果返回大量数据,还有回表,这时应该考虑通过创建组合索引消除回表或者使用全表扫描代替。


4、INDEX SKIP SCAN表示索引跳跃扫描,单块读。返回的数据是有序的(默认升序)。

HINT: INDEX_SS(表名/别名 索引名)。当组合索引的引导列(第一个列)没有在where条件中,并且组合索引的引导列/前几个列的基数很低,where过滤条件对组合索引中非引导列进行过滤的时候就会发生索引跳跃扫描,等待事件为db file sequential read。


在测试表test上创建如下索引:

SQL&get; create index idx_ownerid on test(owner,object_id);

Index created.


删除object_id列上的索引IDX_ID:

SQL&get; drop index idx_id;

Index dropped.


执行如下SQL并且查看执行计划:

SQL&get; select * from test where object_id<100;

98 rows selected.


Execution Plan

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

Plan hash value: 847134193

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

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

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

|   0 | SELECT STATEMENT            |             |    96 |  9312 |   100   (0)| 00:00:02 

|   1 |  TABLE ACCESS BY INDEX ROWID| TEST        |    96 |  9312 |   100   (0)| 00:00:02 

|*  2 |   INDEX SKIP SCAN           | IDX_OWNERID |    96 |       |    97   (0)| 00:00:02 

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

Predicate Information (identified by operation id):

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

   2 - access("OBJECT_ID"<100)

       filter("OBJECT_ID"<100)

结论:

INDEX SKIP SCAN中有个SKIP关键字,也就是说它是跳着扫描的。那么想要跳跃扫描,必须是组合索引,如果是单列索引怎么跳?

其次,组合索引的引导列不能出现在where条件中,如果引导列出现在where条件中,它干嘛还跳跃扫描呢,直接INDEX RANGE SCAN不就得了。

其次要引导列基数很低,如果引导列基数很高,那么它“跳”的次数就多了,性能就差了。

当执行计划中出现了INDEX SKIP SCAN,可以直接在过滤列上面建立索引,使用INDEX RANGE SCAN代替INDEX SKIP SCAN。

从执行计划中看到上面SQL走了索引跳跃扫描。最理想的情况应该是直接走where条件列object_id上的索引,并且走INDEX RANGE SCAN。

但是因为where条件列上面没有直接创建索引,而是间接的被包含在组合索引中,为了避免全表扫描,CBO就选择了索引跳跃扫描。


5、INDEX FULL SCAN表示索引全扫描,单块读,返回的数据是有序的(默认升序)。HINT: INDEX(表名/别名 索引名)。

索引全扫描会扫描索引中所有的叶子块(从左往右扫描),如果索引很大,会产生严重性能问题(因为是单块读),等待事件为db file sequential read。

它通常发生在下面三种情况:

①分页语句,分页语句在本书第八章中会详细介绍,这里不做赘述。

②SQL语句有order by选项,order by 的列都包含在索引中,并且order by 后列顺序必须和索引列顺序一致,order by的第一个列不能有过滤条件,如果有过滤条件就会走索引范围扫描(INDEX RANGE SCAN),并且表的数据量不能太大(数据量太大会走TABLE ACCESS FULL + SORT ORDER BY)。

有如下SQL:

select * from test order by object_id,owner;  


    创建如下索引(索引顺序必须与排序顺序一致,加0是为了让索引能存NULL):


SQL&get; create index idx_idowner on test(object_id,owner,0);


Index created.


    执行如下SQL并且查看执行计划:


SQL&get; select * from test order by object_id,owner;


72462 rows selected.


Execution Plan

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

Plan hash value: 3870803568


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

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

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

|   0 | SELECT STATEMENT            |             | 73020 |  6916K|  1338   (1)| 00:00:17 |

|   1 |  TABLE ACCESS BY INDEX ROWID| TEST        | 73020 |  6916K|  1338   (1)| 00:00:17 |

|   2 |   INDEX FULL SCAN           | IDX_IDOWNER | 73020 |       |   242   (1)| 00:00:03 |

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



? 在进行SORT MERGE JOIN的时候,如果表数据量比较小,让连接列走INDEX FULL SCAN可以避免排序。例如:


SQL&get; select /*+ use_merge(e,d) */

  2   *

  3    from emp e, dept d

  4   where e.deptno = d.deptno;


14 rows selected.



Execution Plan

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

Plan hash value: 844388907


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

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

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

|   0 | SELECT STATEMENT             |         |    14 |   812 |     6  (17)| 00:00:01 |

|   1 |  MERGE JOIN                  |         |    14 |   812 |     6  (17)| 00:00:01 |

|   2 |   TABLE ACCESS BY INDEX ROWID| DEPT    |     4 |    80 |     2   (0)| 00:00:01 |

|   3 |    INDEX FULL SCAN           | PK_DEPT |     4 |       |     1   (0)| 00:00:01 |

|*  4 |   SORT JOIN                  |         |    14 |   532 |     4  (25)| 00:00:01 |

|   5 |    TABLE ACCESS FULL         | EMP     |    14 |   532 |     3   (0)| 00:00:01 |

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


Predicate Information (identified by operation id):

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


   4 - access("E"."DEPTNO"="D"."DEPTNO")

       filter("E"."DEPTNO"="D"."DEPTNO")

当看到执行计划中有INDEX FULL SCAN,首先要检查INDEX FULL SCAN是否有回表。

    如果INDEX FULL SCAN没有回表,要检查索引段大小,如果索引段太大(上GB级别),应该使用INDEX FAST FULL SCAN代替INDEX FULL SCAN,因为INDEX FAST FULL SCAN是多块读,INDEX FULL SCAN是单块读,即使使用了INDEX FAST FULL SCAN会产生额外的排序操作,也要用INDEX FAST FULL SCAN代替INDEX FULL SCAN。

    如果INDEX FULL SCAN有回表,大多数情况下,这种执行计划是错误的,因为INDEX FULL SCAN是单块读,回表也是单块读,这时应该走全表扫描,因为全表扫描是多块读。如果是分页语句走了INDEX FULL SCAN然后回表,这时应该没有太大问题,具体原因请各位读者阅读本书第八章分页语句优化思路。

    

6、INDEX FAST FULL SCAN 表示索引快速全扫描,多块读。

HINT:INDEX_FFS(表名/别名 索引名)。

当需要从表中查询出大量数据,但是只需要获取表中部分列的数据,那么这时可以利用索引快速全扫描代替全表扫描来提升性能。

索引快速全扫描的扫描方式与全表扫描的扫描方式是一样,都是按区扫描,所以它可以多块读,并且可以并行扫描。

等待事件为db file scattered read,如果是并行扫描,等待事件为direct path read。


现有如下SQL:


select owner,object_name from test;


上面SQL没有过滤条件,默认情况下会走全表扫描,但是因为Oracle是行存储数据库,全表扫描的时候会扫描表中所有的列,而上面查询只访问表中2个列,全表扫描会多扫描额外13个列,因此可以创建一个组合索引,使用索引快速全扫描代替全表扫描:


SQL&get; create index idx_ownername on test(owner,object_name,0);


Index created. 


查看SQL执行计划:

SQL&get; select owner,object_name from test;

72462 rows selected.


Execution Plan

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

Plan hash value: 3888663772


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

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

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

|   0 | SELECT STATEMENT     |               | 73020 |  2210K|    79   (2)| 00:00:01 |

|   1 |  INDEX FAST FULL SCAN| IDX_OWNERNAME | 73020 |  2210K|    79   (2)| 00:00:01 |

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

当需要从表中查询出大量数据,但是只需要获取表中部分列的数据,那么这时可以利用索引快速全扫描代替全表扫描来提升性能。

现有如下SQL:


select object_name from test where object_id<100;

上面SQL有过滤条件,根据过滤条件where object_id<100过滤数据之后只返回少量数据,一般情况下直接在object_id列创建索引,让上面SQL走object_id列的索引即可:

SQL&get; create index idx_id on test(object_id);


Index created.


SQL&get; select object_name from test where object_id<100;


98 rows selected.


Execution Plan

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

Plan hash value: 3946039639


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

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

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

|   0 | SELECT STATEMENT            |        |    96 |  2880 |     4   (0)| 00:00:01 |

|   1 |  TABLE ACCESS BY INDEX ROWID| TEST   |    96 |  2880 |     4   (0)| 00:00:01 |

|*  2 |   INDEX RANGE SCAN          | IDX_ID |    96 |       |     2   (0)| 00:00:01 |

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


Predicate Information (identified by operation id):

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


   2 - access("OBJECT_ID"<100)



Statistics

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

          0  recursive calls

          0  db block gets

         18  consistent gets

          0  physical reads

          0  redo size

       2217  bytes sent via SQL*Net to client

        485  bytes received via SQL*Net from client

          8  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

         98  rows processed    

因为上面SQL只查询一个字段,因此可以将select列放到组合索引中,避免回表:


SQL&get; create index idx_idname on test(object_id,object_name);


Index created.


    再次查看SQL的执行计划:


SQL&get; select object_name from test where object_id<100;


98 rows selected.



Execution Plan

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

Plan hash value: 3678957952


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

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

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

|   0 | SELECT STATEMENT |            |    96 |  2880 |     2   (0)| 00:00:01 |

|*  1 |  INDEX RANGE SCAN| IDX_IDNAME |    96 |  2880 |     2   (0)| 00:00:01 |

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


Predicate Information (identified by operation id):

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


   1 - access("OBJECT_ID"<100)



Statistics

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

          0  recursive calls

          0  db block gets

          9  consistent gets

          0  physical reads

          0  redo size

       2217  bytes sent via SQL*Net to client

        485  bytes received via SQL*Net from client

          8  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

         98  rows processed

现有如下SQL:


select object_name from test where object_id&get;100;


    上面SQL过滤条件是where object_id&get;100,返回大量数据,应该走全表扫描,但是因为上面SQL只访问一个字段,因此可以走索引快速全扫描来代替全表扫描:


SQL&get; select object_name from test where object_id&get;100;


72363 rows selected.



Execution Plan

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

Plan hash value: 252646278


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

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

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

|   0 | SELECT STATEMENT     |            | 72924 |  2136K|    73   (2)| 00:00:01 |

|*  1 |  INDEX FAST FULL SCAN| IDX_IDNAME | 72924 |  2136K|    73   (2)| 00:00:01 |

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


Predicate Information (identified by operation id):

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


   1 - filter("OBJECT_ID"&get;100)

有读者可能会有疑问,上面SQL能否走INDEX RANGE SCAN呢?INDEX RANGE SCAN是单块读,上面SQL会返回表中大量数据,“几乎”会扫描索引中所有的叶子块。INDEX FAST FULL SCAN是多块读,会扫描索引中所有的块(根块,所有的分支块,所有的叶子块),虽然INDEX RANGE SCAN相比INDEX FAST FULL SCAN扫描的块少(逻辑读少),但是INDEX RANGE SCAN是单块读,耗费的I/O次数比INDEX FAST FULL SCAN的I/O次数多,所以INDEX FAST FULL SCAN性能更好。

    在做SQL优化的时候,不要全看逻辑读来判断一个SQL性能的好坏,物理I/O次数比逻辑读更为重要。有时候逻辑读高的执行计划性能反而比逻辑读低的执行计划性能更好,因为逻辑读高的执行计划物理I/O次数比逻辑读低的执行计划物理I/O次数低。

    在Oracle数据库中,INDEX FAST FULL SCAN是用来代替TABLE ACCESS FULL的。因为Oracle是行存储数据库,TABLE ACCESS FULL会扫描表中所有的列,而INDEX FAST FULL SCAN只需要扫描表中部分列,INDEX FAST FULL SCAN就是因为Oracle是行存储这个“缺陷”而产生的。

    如果数据库是Exadata,INDEX FAST FULL SCAN几乎没有用武之地,因为Exadata是行列混合存储,在全表扫描的时候可以只扫描需要的列(Smart Scan),这时就没必要使用INDEX FAST FULL SCAN来代替全表扫描了。在Exadata中如果强行使用INDEX FAST FUL SCAN来代替全表扫描,这时反而会降低数据库性能,因为没办法使用Exadata中的Smart Scan。

    如果启用了12c中的新特性IN MEMORY OPTION,INDEX FAST FULL SCAN几乎也没有用武之地了,因为表中的数据可以以列的形式存放在内存中,这时直接访问内存中的数据即可。



7、INDEX FULL SCAN (MIN/MAX)               

    INDEX FULL SCAN (MIN/MAX)表示索引最小/最大值扫描,单块读,该访问路径发生在 SELECT MAX(COLUMN) FROM TABLE 或者SELECT MIN(COLUMN) FROM TABLE等SQL语句中。

    INDEX FULL SCAN (MIN/MAX)只会访问“索引高度”个索引块,其性能与INDEX UNIQUE SCAN一样,仅次于TABLE ACCESS BY USER ROWID。

    现有如下SQL:


select max(object_id) from t;


    该SQL查询object_id的最大值,如果object_id列有索引,索引默认是升序排序的,这时只需要扫描索引中“最右边”的叶子块就能得到object_id的最大值。现在查看上面SQL的执行计划:


SQL&get; select max(object_id) from t;


Elapsed: 00:00:00.00


Execution Plan

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

Plan hash value: 2448092560


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

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

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

|   0 | SELECT STATEMENT           |          |     1 |    13 |   186   (1)| 00:00:03 |

|   1 |  SORT AGGREGATE            |          |     1 |    13 |            |          |

|   2 |   INDEX FULL SCAN (MIN/MAX)| IDX_T_ID | 67907 |   862K|            |          |

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


Note

-----

   - dynamic sampling used for this statement (level=2)



Statistics

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

          0  recursive calls

          0  db block gets

          2  consistent gets

          0  physical reads

          0  redo size

        430  bytes sent via SQL*Net to client

        419  bytes received via SQL*Net from client

          2  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

          1  rows processed

现有另外一个SQL:


  select max(object_id),min(object_id) from t;


该SQL要同时查看object_id的最大值和最小值,如果想直接从object_id列的索引获取数据,只需要扫描索引中“最左边”和“最右边”的叶子块就可以。在Btree索引中,索引叶子块是双向指向的,如果要一次性获取索引中“最左边”和“最右边”的叶子块,就需要连带的扫描“最大值”与“最小值”中间的叶子块,而本案例中,中间叶子块的数据并不是我们需要的。如果上面SQL走索引,会走INDEX FAST FULL SCAN,而不会走INDEX FULL SCAN,因为INDEX FAST FULL SCAN可以多块读,而INDEX FULL SCAN是单块读,两者性能差距巨大(如果索引已经缓存在buffer cache中,走INDEX FULL SCAN与INDEX FAST FULL SCAN效率几乎一样,因为不需要物理I/O)。需要注意的是,上面SQL没有排除object_id为null,如果直接运行上面SQL,不会走索引:


SQL&get; select max(object_id),min(object_id) from t;

Elapsed: 00:00:00.02


Execution Plan

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

Plan hash value: 2966233522


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

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

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

|   0 | SELECT STATEMENT   |      |     1 |    13 |   186   (1)| 00:00:03 |

|   1 |  SORT AGGREGATE    |      |     1 |    13 |            |          |

|   2 |   TABLE ACCESS FULL| T    | 67907 |   862K|   186   (1)| 00:00:03 |

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


    排除object_id为null,查看执行计划:


SQL&get; select max(object_id),min(object_id) from t where object_id is not null;


Elapsed: 00:00:00.01


Execution Plan

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

Plan hash value: 3570898368


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

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

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

|   0 | SELECT STATEMENT      |          |     1 |    13 |    33   (4)| 00:00:01 |

|   1 |  SORT AGGREGATE       |          |     1 |    13 |            |          |

|*  2 |   INDEX FAST FULL SCAN| IDX_T_ID | 67907 |   862K|    33   (4)| 00:00:01 |

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


Predicate Information (identified by operation id):

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


   2 - filter("OBJECT_ID" IS NOT NULL)


Note

-----

   - dynamic sampling used for this statement (level=2)



Statistics

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

          0  recursive calls

          0  db block gets

        169  consistent gets

          0  physical reads

          0  redo size

        501  bytes sent via SQL*Net to client

        419  bytes received via SQL*Net from client

          2  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

          1  rows processed


从上面执行计划中看到SQL走了INDEX FAST FULL SCAN,INDEX FAST FULL SCAN会扫描索引段中所有的块,理想的情况是只扫描索引中“最左边”和“最右边”的叶子块。现在将上面SQL改写为如下SQL:


select (select max(object_id) from t),(select min(object_id) from t) from dual;


查看后的执行计划:

SQL&get; select (select max(object_id) from t),(select min(object_id) from t) from dual;

Elapsed: 00:00:00.01


Execution Plan

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

Plan hash value: 3622839313


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

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

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

|   0 | SELECT STATEMENT           |          |     1 |       |     2   (0)| 00:00:01 |

|   1 |  SORT AGGREGATE            |          |     1 |    13 |            |          |

|   2 |   INDEX FULL SCAN (MIN/MAX)| IDX_T_ID | 67907 |   862K|            |          |

|   3 |  SORT AGGREGATE            |          |     1 |    13 |            |          |

|   4 |   INDEX FULL SCAN (MIN/MAX)| IDX_T_ID | 67907 |   862K|            |          |

|   5 |  FAST DUAL                 |          |     1 |       |     2   (0)| 00:00:01 |

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


Note

-----

   - dynamic sampling used for this statement (level=2)

Statistics

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

          0  recursive calls

          0  db block gets

          4  consistent gets

          0  physical reads

          0  redo size

        527  bytes sent via SQL*Net to client

        419  bytes received via SQL*Net from client

          2  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

          1  rows processed  


原始SQL因为需要1次性从索引中取得最大值和最小值,所以导致走了INDEX FAST FULL SCAN。将SQL进行等价改写之后,访问了索引2次,一次取最大值,一次取最小值,从而避免扫描不需要的索引叶子块,大大提升了查询性能。



####以上讲解出自 道森培训落落讲师

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/30327022/viewspace-2168770/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2015-07-04

  • 博文量
    64
  • 访问量
    45788