如何解决Oracle Optimizer 无关过滤谓词?
为什么即使在同一索引的访问谓词保证过滤谓词始终为真之后,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 企业版。
解决方法
这是“传递闭包”转换的结果:您可以在此处阅读更多信息:
- Transitivity and Transitive Closure (Doc ID 68979.1) Doc id 68979.1
- Jonathan Lewis - Cartesian Merge Join
- Jonathan Lewis - Transitive Closure(或者,甚至更好,在他的书中"Cost Based Oracle Fundamentals")
如果您获得 CBO 跟踪(rgb(255,0)
或 alter session set events '10053 trace name context forever,level 1'
),您将看到转换发生在选择连接方法和访问路径之前。它允许 CBO 分析更多不同的访问路径并选择最佳可用计划。此外,在自适应计划的情况下,它允许 oracle 即时更改连接方法。
例如,您可以获得这样的计划:
alter session set events 'trace[SQL_Optimizer.*]
事实上,您可以使用事件 ----------------------------------------------------------------------------------------------------------------
| 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');
如您所见,该谓词已消失。
附注。传递谓词的其他事件:
- ORA-10155:CBO 禁用传递 OR 链的生成
- ORA-10171:CBO 禁用传递连接谓词
- ORA-10179:CBO 关闭传递谓词替换
- ORA-10195:CBO 不对传递谓词使用检查约束
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。