因复合索引而更新答案
我决定根据您指定复合索引的 cmets 更新答案。
- 索引全扫描按 SORTED 顺序读取每个索引节点。
- 索引快速全扫描用于以 UNSORTED 顺序从索引中检索表行。
索引全扫描:当 CBO 统计数据表明全索引扫描将比全表扫描更有效并且排序或组是在结果集上完成。当 CBO 确定查询将按索引顺序返回大量行时,通常会调用全索引扫描,并且全表扫描以及 sort 或 group by 选项可能会导致磁盘排序或散列操作转到临时表空间.
快速全索引扫描 当索引包含满足查询所需的所有值并且不需要表访问时,将调用此执行计划。快速全索引扫描执行计划将通过多块读取(使用 db_file_multiblock_read_count)读取整个索引,并以未排序的顺序返回行。
在您的具体情况下,它应该使用 FAST FULL SCAN,但可能是统计问题或 optimizer_index_cost_adj 参数值太低。
让我给你看一个测试用例。我创建了一个表,其中 id 字段生成为身份,一个字段包含性别,M 或 F,以及列 c1 的随机字符串。
SQL> create table test_index ( c1 varchar2(10), c2 number generated always as identity, c3 varchar2(1) );
Table created.
填充 200000 条记录并在 c2 和 c3 上创建索引组合
SQL> declare
2 begin
3 for r in 1 .. 100000
4 loop
5 insert into test_index ( c1 , c3 ) values ( dbms_random.string('U',1
6 insert into test_index ( c1 , c3 ) values ( dbms_random.string('U',1
7 end loop;
8 commit;
9 end;
10 /
PL/SQL procedure successfully completed.
SQL> create index idx_test_index on test_index (c2 , c3) ;
Index created.
现在,让我们看看它的表现如何
SQL> select count(*) from cpl_rep.test_index ;
COUNT(*)
----------
200000
SQL> exec dbms_stats.gather_table_stats ( 'MYOWNER', 'TEST_INDEX' , cascade => true );
PL/SQL procedure successfully completed.
SQL> set autotrace traceonly explain
SQL> set lines 200
SQL> select c3, count(*) from test_index group by c3
Execution Plan
----------------------------------------------------------
Plan hash value: 805205005
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 4 | 102 (8)| 00:00:01 |
| 1 | HASH GROUP BY | | 2 | 4 | 102 (8)| 00:00:01 |
| 2 | INDEX FAST FULL SCAN| IDX_TEST_INDEX | 200K| 390K| 95 (2)| 00:00:01 |
----------------------------------------------------------------------------------------
SQL> select /*+index ( a IDX_TEST_INDEX ) */ c3, count(*) from myowner.test_index a group by c3
2 ;
Execution Plan
----------------------------------------------------------
Plan hash value: 2605845939
-----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 4 | 257 (4)| 00:00:01 |
| 1 | HASH GROUP BY | | 2 | 4 | 257 (4)| 00:00:01 |
| 2 | INDEX FULL SCAN| IDX_TEST_INDEX | 200K| 390K| 250 (1)| 00:00:01 |
-----------------------------------------------------------------------------------
在我的示例中,由于 optimizer_index_cost_adj 的值,它使用 FAST FULL SCAN
SQL> set autotrace off
SQL> show parameter index_cost_adj
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
optimizer_index_cost_adj integer 100
让我们把它改成低值看看会发生什么
SQL> alter session set optimizer_index_cost_adj=20 ;
Session altered.
SQL> set autotrace traceonly explain
SQL> select c3, count(*) from cpl_rep.test_index group by c3 ;
Execution Plan
----------------------------------------------------------
Plan hash value: 2605845939
-----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 4 | 57 (13)| 00:00:01 |
| 1 | HASH GROUP BY | | 2 | 4 | 57 (13)| 00:00:01 |
| 2 | INDEX FULL SCAN| IDX_TEST_INDEX | 200K| 390K| 50 (0)| 00:00:01 |
-----------------------------------------------------------------------------------
创建了 optimizer_index_cost_adj 参数以允许更改全扫描与索引操作的相对成本。这是所有参数中最重要的,默认设置 100 对于大多数 Oracle 系统来说是不正确的。它可以让您调整优化器行为以使访问路径选择或多或少对索引友好——也就是说,使优化器或多或少倾向于在全表扫描或全索引扫描上选择索引访问路径。
此参数的默认值为 100%,此时优化器以常规成本评估索引访问路径。任何其他值都会使优化器以常规成本的该百分比评估访问路径。例如,设置为 50 会使索引访问路径看起来是正常开销的一半。