Oracle 优化器无关的过滤谓词?

Oracle Optimizer Extraneous Filter Predicate?

为什么即使同一索引的访问谓词保证过滤谓词始终为真,Oracle 仍然在索引上应用过滤谓词?

drop table index_filter_child
;

drop table index_filter_parent
;

create table index_filter_parent 
  as
  select level id, chr(mod(level - 1, 26) + ascii('A')) code from dual connect by level <= 26
;

create table index_filter_child 
  as
  with
    "C" as (select chr(mod(level - 1, 26) + ascii('A')) code from dual connect by level <= 26)
    select rownum id, C1.code from C C1, C C2
;

exec dbms_stats.gather_table_stats('USER','INDEX_FILTER_PARENT')
;

exec dbms_stats.gather_table_stats('USER','INDEX_FILTER_CHILD')
;

create index ix_index_filter_parent on index_filter_parent(code)
;

create index ix_index_filter_child on index_filter_child(code)
;

select P.* 
  from index_filter_parent "P" 
       join index_filter_child "C" 
         on C.code = P.code
where P.code in('A','Z') --same result if we predicate instead on C.code in('A','Z')
;


--------------------------------------------------------------------------------------------------------------
| id  | Operation                     | name                         | rows  | Bytes | cost (%CPU)| time     |
--------------------------------------------------------------------------------------------------------------
|   0 | select statement              |                              |     5 |    35 |     4   (0)| 00:00:01 |
|   1 |  nested LOOPS                 |                              |     5 |    35 |     4   (0)| 00:00:01 |
|   2 |   INLIST ITERATOR             |                              |       |       |            |          |
|   3 |    table access by index ROWID| INDEX_FILTER_PARENT          |     2 |    10 |     2   (0)| 00:00:01 |
|*  4 |     index range scan          | IX_INDEX_FILTER_PARENT       |     2 |       |     1   (0)| 00:00:01 |
|*  5 |   index range scan            | IX_INDEX_FILTER_CHILD        |     2 |     4 |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("P"."CODE"='A' or "P"."CODE"='Z')
   5 - access("C"."CODE"="P"."CODE")
       filter("C"."CODE"='A' or "C"."CODE"='Z')   <========== why is this needed? 

鉴于 access("C"."CODE"="P"."CODE") 保证 C.code'A''Z',为什么需要 5 中的过滤谓词?

提前致谢。

Oracle 12.1企业版。

这是“传递闭包”转换的结果:您可以在此处阅读更多信息:

  1. Transitivity and Transitive Closure (Doc ID 68979.1) Doc id 68979.1
  2. Jonathan Lewis - Cartesian Merge Join
  3. Jonathan Lewis - Transitive Closure (or, even better, in his book "Cost Based Oracle Fundamentals")

如果您获得 CBO 跟踪(alter session set events '10053 trace name context forever, level 1'alter session set events 'trace[SQL_Optimizer.*]),您将看到转换发生在选择连接方法和访问路径之前。它允许 CBO 分析更多不同的访问路径并选择最佳可用计划。此外,在自适应计划的情况下,它允许 oracle 动态更改连接方法。 例如,您可以获得这样的计划:

----------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name                   | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |                        |    52 |   364 |     4   (0)| 00:00:01 |
|*  1 |  HASH JOIN                            |                        |    52 |   364 |     4   (0)| 00:00:01 |
|   2 |   INLIST ITERATOR                     |                        |       |       |            |          |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED| INDEX_FILTER_PARENT    |     2 |    10 |     2   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN                  | IX_INDEX_FILTER_PARENT |     2 |       |     1   (0)| 00:00:01 |
|   5 |   INLIST ITERATOR                     |                        |       |       |            |          |
|*  6 |    INDEX RANGE SCAN                   | IX_INDEX_FILTER_CHILD  |    52 |   104 |     2   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("C"."CODE"="P"."CODE")
   4 - access("P"."CODE"='A' OR "P"."CODE"='Z')
   6 - access("C"."CODE"='A' OR "C"."CODE"='Z')

事实上,您可以使用事件 10155: CBO disable generation of transitive OR-chains.

禁用它

你的例子:

alter session set events '10155';
explain plan for
select P.* 
  from index_filter_parent "P" 
       join index_filter_child "C" 
         on C.code = P.code
where P.code in('A','Z');

结果:

SQL> alter session set events '10155';

Session altered.

SQL> explain plan for
  2  select P.*
  3    from index_filter_parent "P"
  4         join index_filter_child "C"
  5           on C.code = P.code
  6  where P.code in('A','Z') ;

Explained.

SQL> @xplan typical


PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------
Plan hash value: 2543178509

----------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name                   | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |                        |    52 |   364 |     4   (0)| 00:00:01 |
|   1 |  NESTED LOOPS                         |                        |    52 |   364 |     4   (0)| 00:00:01 |
|   2 |   INLIST ITERATOR                     |                        |       |       |            |          |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED| INDEX_FILTER_PARENT    |     2 |    10 |     2   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN                  | IX_INDEX_FILTER_PARENT |     2 |       |     1   (0)| 00:00:01 |
|*  5 |   INDEX RANGE SCAN                    | IX_INDEX_FILTER_CHILD  |    26 |    52 |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("P"."CODE"='A' OR "P"."CODE"='Z')
   5 - access("C"."CODE"="P"."CODE")

Note
-----
   - this is an adaptive plan

22 rows selected.

如您所见,谓词已消失。

PS。传递谓词的其他事件:

  • ORA-10155: CBO 禁止生成可传递的 OR 链
  • ORA-10171: CBO 禁用传递连接谓词
  • ORA-10179: CBO 关闭传递谓词替换
  • ORA-10195: CBO 不对传递谓词使用检查约束