Index Full Scan 索引全扫描 (page 103)
An index full scan is chosen under several conditions including: when there is no predicate but the
column list can be satisfied through an index on a column, the predicate contains a condition on a
non-leading column in an index, or the data can be retrieved via an index in sorted order and save the
need for a separate sort step. Listing 3-13 shows an example of each of these cases.
选中索引全扫描包括下列几种条件:没有谓词但是可通过列上的索引满足(查询)列集,谓词包含一个在索引中非前置列上的条件,或者数据能通过索引按顺序检索而省去了单独排序的步骤。
Listing 3-13. Index Full Scan Examples
SQL> set autotrace traceonly explain
SQL> select email from hr.employees ;
--HR EMP_EMAIL_UK Unique EMAIL
Execution Plan
----------------------------------------------------------
Plan hash value: 2196514524
----------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 107 | 856 | 1 (0) |
| 1 | INDEX FULL SCAN | EMP_EMAIL_UK | 107 | 856 | 1 (0) |
----------------------------------------------------------------------
SQL>
SQL> select first_name, last_name from hr.employees
2 where first_name like 'A%' ;
--HR EMP_NAME_IX Normal LAST_NAME, FIRST_NAME
Execution Plan
----------------------------------------------------------
Plan hash value: 2228653197
---------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
---------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3 | 45 | 1 (0) |
|* 1 | INDEX FULL SCAN | EMP_NAME_IX | 3 | 45 | 1 (0) |
---------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("FIRST_NAME" LIKE 'A%')
filter("FIRST_NAME" LIKE 'A%')
SQL> select * from hr.employees order by employee_id ;
Execution Plan
----------------------------------------------------------
Plan hash value: 2186312383
----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 107 | 7276 | 3 (0) |
| 1 | TABLE ACCESS BY INDEX ROWID | EMPLOYEES | 107 | 7276 | 3 (0) |
| 2 | INDEX FULL SCAN | EMP_EMP_ID_PK | 107 | | 1 (0) |
----------------------------------------------------------------------------------
SQL> select * from hr.employees order by employee_id desc ;
Execution Plan
----------------------------------------------------------
Plan hash value: 2761389396
----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 107 | 7276 | 3 (0) |
| 1 | TABLE ACCESS BY INDEX ROWID | EMPLOYEES | 107 | 7276 | 3 (0) |
| 2 | INDEX FULL SCAN DESCENDING | EMP_EMP_ID_PK | 107 | | 1 (0) |
----------------------------------------------------------------------------------
An index full scan operation will scan every leaf block in the index structure, read the rowids for
each entry, and retrieve the table rows. Every leaf block is accessed. This is often more efficient than
doing a full table scan as the index blocks will contain more entries than the table blocks will,
therefore fewer overall blocks may need to be accessed. In cases where the columns needed to satisfy
the column list are all present as part of the index entry, the table access step is avoided as well. This
means that choosing an index full scan operation will be more efficient than reading all the table
blocks.
索引全扫描操作将扫描在索引结构中的每个叶块,读取每条记录的rowid,检索表的行集。每个叶块都被访问到。这通常都比做全表扫描更加有效率因为索引块比表块包含更多的记录。因此总体上需要访问的块要少一些。 如果列集中所需的列都是索引记录的一部分,还可以省略表访问步骤。 这意味着选择索引全扫描操作将比读取所有表块更有效率。
You may have noticed in the last example that the index full scan operation also has the ability to
read in descending order to avoid the need for a separate descending ordered sort request. There is
another optimization for index full scans. This optimization occurs when a query requests the
minimum or maximum column value and that column is indexed. Listing 3-14 shows an example of
this operation choice.
你可能注意到在上个例子中索引全扫描操作能降序读入而避免需要单独的降序排序请求。索引全扫描还有另一个优化。当查询请求某具有索引的列上的最小或最大列值时,这个优化就会发生。列表3-14展示了这个操作选择的例子。
Listing 3-14. Index Full Scan Min/Max Optimization
SQL> set autotrace traceonly explain
SQL> select min(department_id) from hr.employees ;
--HR EMP_DEPARTMENT_IX Normal DEPARTMENT_ID
Execution Plan
----------------------------------------------------------
Plan hash value: 613773769
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 3 | 3(0) |
| 1 | SORT AGGREGATE | | 1 | 3 | |
| 2 | INDEX FULL SCAN (MIN/MAX) | EMP_DEPARTMENT_IX | 107 | 321 | |
-------------------------------------------------------------------------------------
SQL> select max(department_id) from hr.employees ;
Execution Plan
----------------------------------------------------------
Plan hash value: 613773769
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 3 | 3 (0) |
| 1 | SORT AGGREGATE | | 1 | 3 | |
| 2 | INDEX FULL SCAN (MIN/MAX) | EMP_DEPARTMENT_IX | 107 | 321 | |
-------------------------------------------------------------------------------------
SQL> select min(department_id), max(department_id) from hr.employees ;
Execution Plan
----------------------------------------------------------
Plan hash value: 1756381138
---------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
---------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 3 | 3 (0) |
| 1 | SORT AGGREGATE | | 1 | 3 | |
| 2 | TABLE ACCESS FULL | EMPLOYEES | 107 | 321 | 3 (0) |
---------------------------------------------------------------------
SQL> select (select min(department_id) from hr.employees) min_id,
2 (select max(department_id) from hr.employees) max_id
3 from dual
4
Execution Plan
----------------------------------------------------------
Plan hash value: 2189307159
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 2 (0)|
| 1 | SORT AGGREGATE | | 1 | 3 | |
| 2 | INDEX FULL SCAN (MIN/MAX)| EMP_DEPARTMENT_IX | 107 | 321 | |
| 3 | SORT AGGREGATE | | 1 | 3 | |
| 4 | INDEX FULL SCAN (MIN/MAX)| EMP_DEPARTMENT_IX | 107 | 321 | |
| 5 | FAST DUAL | | 1 | | 2 (0)|
-------------------------------------------------------------------------------------
As the example shows, when a MIN or MAX aggregate is requested, the optimizer can choose a special optimized version of the index full scan operation. In these special cases, when the index is used to quickly retrieve the minimum value, it will be the first entry in the first index leaf block; when it
retrieves the maximum value, it will be the last entry in the last index leaf block. This makes perfect
sense as the index is stored in sorted order so the minimum and maximum values have to be at either
end of the first and last leaf blocks. But the really great part is that in these special cases, the index full
scan isn’t really a full scan—it is a scan of only root block, one or more branch blocks, and first or last
leaf blocks. This means that finding these values is very fast and very low cost in terms of the number
of block accesses required. While the index full scan operation title may make it seem a bit confusing
as index full scans typically read all the index leaf blocks, this optimization is a nice win in terms of
performance.
如例子所示,当请求MIN和MAX聚合函数,优化器就能选择特别优化版的索引全扫描操作。在这些特殊的案例中,当索引用于快速检索最小值,它将是第一个索引叶块的第一条记录;当它检索的是最大值,它将是最后索引叶块的最后一条记录。这真是很完美,因为索引是按顺序存入的所以最小值、最大值必然在第一个和最后一个叶块的两端。 但是在这些特殊案例中真正了不起的地方是:索引全扫描实际不是全扫描--它只扫描了根块,一个或多个分支块,还有第一个或最后一个叶 块。这就是说找到这些值是非常的快和成本低的,根据所需访问的块数而言。虽然索引全扫描操作标题可能让它似乎有些迷惑,因为索引全扫描一般都是读取所有的索引叶块,这个优化是性能上的美好胜利 。
I did include an example of where the query included both a MIN and a MAX aggregate, and as you
may have noticed, the optimizer chose to do a full table scan with a sort instead of the nice optimized
index full scan operation. While I think this is a short-coming in the way the optimizer handles this
situation, there is a fairly easy way to get the same optimized behavior. Just code the two queries
separately. In this way, you get the benefits of the optimization.
我包含了一个例子,查询中同时包含一个MIN和一个MAX聚合,而你可能注意到,优化器选择的是带排序的全表扫描,而不是精密优化的索引全扫描操作。虽然我想这是优化器处理这种情况的一个缺点 ,但是很容易取得相同的优化行为。只要写两个单独的查询。这样,你就能受益于该优化。