为什么 oracle 选择 INDEX FULL SCAN 然后是 ACCESS BY INDEX ROWID vs FULL TABLE SCAN?

Why does oracle chose INDEX FULL SCAN followed by ACCESS BY INDEX ROWID vs FULL TABLE SCAN?

为什么 oracle 选择 INDEX FULL SCAN,然后选择 ACCESS BY INDEX ROWID,而不是仅执行完整的 table 扫描,这是一个步骤,做同样的事情,而且可能更快?

甲骨文为什么选择

|   2 |   TABLE ACCESS BY INDEX ROWID
|   3 |    INDEX FULL SCAN    

超过

|  2 |  TABLE ACCESS FULL| 

为了进一步说明,这是查询和完整的执行计划

SELECT EMP_NO, ENAME, SALARY, dname 
FROM EMP E, DEPT D
WHERE E.DEPT_NO=D.DEPT_NO;



Execution Plan
----------------------------------------------------------
Plan hash value: 2125045483

----------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |         |   879 | 35160 |     8  (13)| 00:00:01 |
|   1 |  MERGE JOIN                  |         |   879 | 35160 |     8  (13)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID| DEPT    |     7 |    91 |     2   (0)| 00:00:01 |
|   3 |    INDEX FULL SCAN           | DEPT_PK |     7 |       |     1   (0)| 00:00:01 |
|*  4 |   SORT JOIN                  |         |   879 | 23733 |     6  (17)| 00:00:01 |
|   5 |    TABLE ACCESS FULL         | EMP     |   879 | 23733 |     5   (0)| 00:00:01 |
----------------------------------------------------------------------------------------

您会注意到优化器 确实 在 EMP table 上选择了完整 TABLE 扫描。然后它使用键 EMP.DEPT_NO 在 DEPT 上查找适当的值,因为在这种情况下进行 879 次查找显然比进行 FULL TABLE SCAN 更快(至少在优化器看来)在 DEPT 上进行 879*7 次比较。

为了好玩,您可以通过查看 USER_TABLES 或 DBA_TABLES 视图来了解最近如何收集这些 table 的统计信息:

SELECT TABLE_NAME, LAST_ANALYZED
  FROM USER_TABLES
  WHERE TABLE_NAME IN ('EMP', 'DEPT')

如果您愿意,可以使用

收集这些 table 的最新统计数据
BEGIN
  DBMS_STATS.GATHER_TABLE_STATISTICS(OWNNAME => 'your_schema_name',
                                     TABNAME => 'EMP',
                                     CASCADE => TRUE);

  DBMS_STATS.GATHER_TABLE_STATISTICS(OWNNAME => 'your_schema_name',
                                     TABNAME => 'DEPT',
                                     CASCADE => TRUE);
END;

通常,table 完整扫描必须读取到 table 高水位线(即几乎每个已分配的块都被 table 使用)。如果 table 中有一个大的删除,table 中可能有 10,000 个块,大部分是空的,它必须通读。

索引是一种更复杂的结构,但索引全扫描不必处理空块。此外,索引(因为它们只有列的子集)往往更小并且在缓存中的停留时间更长。

在您的示例中,访问索引和 table 来自索引的总成本为 3。这是相当低的,也许 table 扫描结果是 4 或 5(也很低,但仍然是第二)。