
我提出了“子操作按先后顺序执行,递归向下”的解释执行计划的基本策略,但是
在这个系列的第5部分,我们看了一些需要额外注意的”第一个子操作先执行”的例子。在第6部分中,我们将继续探讨一个原则—“子查询推入”,其中“第一个子操作先执行”可能会导致错误的结论。
Access or Filter
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 19 | 2 |
| 1 | TABLE ACCESS BY INDEX ROWID | MIN_MAX | 1 | 19 | 2 |
|* 2 | INDEX UNIQUE SCAN | MM_PK | 1 | | 1 |
| 3 | SORT AGGREGATE | | 1 | 11 | |
|* 4 | TABLE ACCESS BY INDEX ROWID| MIN_MAX | 1 | 11 | 3 |
|* 5 | INDEX RANGE SCAN | MM_PK | 10 | | 2 |
--------------------------------------------------------------------------
-------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
-------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 19 | 5 |
| 1 | TABLE ACCESS BY INDEX ROWID | MIN_MAX | 1 | 19 | 2 |
|* 2 | INDEX UNIQUE SCAN | MM_PK | 1 | | 1 |
|* 3 | TABLE ACCESS BY INDEX ROWID| MIN_MAX | 1 | 11 | 3 |
|* 4 | INDEX RANGE SCAN | MM_PK | 2 | | 2 |
-------------------------------------------------------------------------
实际上”第一个子操作先执行”对于第一个计划来说是正确的,它会从下到上执行,但是对于第二个计划来说是行不通的,由于优化器呈现的是“推入”过的筛选子查询,所以计划的形状被扭曲了,也就是说,在尽可能早的时间运行。很难从计划的主体中看出这是否已经发生了,你真的需要检查计划的谓词部分,甚至可以参考原始语句来理解发生了什么。
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("MM1"."ID_PARENT"=100 AND "MM1"."ID_CHILD"= (SELECT
MAX("MM2"."ID_CHILD") FROM "MIN_MAX" "MM2"
WHERE "MM2"."ID_PARENT"=100 AND "STATUS"=1))
4 - filter("STATUS"=1)
5 - access("MM2"."ID_PARENT"=100)
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("MM1"."ID_PARENT"=100 AND "MM1"."ID_CHILD"=1)
filter( EXISTS (SELECT /*+ PUSH_SUBQ NO_UNNEST */ 0 FROM
"MIN_MAX" "MM2" WHERE "MM2"."ID_PARENT"=100 AND "STATUS"=1 AND
("MM2"."ID_CHILD"=TRUNC(:B1) OR "MM2"."ID_CHILD"=TRUNC(:B2+1))))
3 - filter("STATUS"=1)
4 - access("MM2"."ID_PARENT"=100)
filter("MM2"."ID_CHILD"=TRUNC(:B1) OR
"MM2"."ID_CHILD"=TRUNC(:B2+1))
注意第2行出现的关键差异——在两个执行计划中的操作都是索引唯一扫描。第一个计划只报告一个访问谓词(包含一个子查询),而第二个计划同时显示一个访问谓词(它由针对索引列的简单谓词组成)和一个筛选谓词(它由一个子查询组成)。
select
small_vc
from min_max mm1
where mm1.id_parent = 100
and mm1.id_child = (
select max(mm2.id_child)
from min_max mm2
where mm2.id_parent = 100
and status = 1
)
;
select
small_vc
from min_max mm1
where mm1.id_parent = 100
and mm1.id_child = 1
and exists (
select /*+ no_unnest push_subq */
null
from min_max mm2
where mm2.id_parent = 100
and (
mm2.id_child = trunc(mm1.id_child)
or mm2.id_child = trunc(mm1.id_child + 1)
)
and status = 1
)
;
------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 19 | 5 |
|* 1 | FILTER | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID| MIN_MAX | 1 | 19 | 2 |
|* 3 | INDEX UNIQUE SCAN | MM_PK | 1 | | 1 |
|* 4 | TABLE ACCESS BY INDEX ROWID| MIN_MAX | 1 | 11 | 3 |
|* 5 | INDEX RANGE SCAN | MM_PK | 2 | | 2 |
------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter( EXISTS (SELECT /*+ NO_PUSH_SUBQ NO_UNNEST */ 0 FROM
"MIN_MAX" "MM2" WHERE "MM2"."ID_PARENT"=100 AND "STATUS"=1 AND
("MM2"."ID_CHILD"=TRUNC(:B1) OR "MM2"."ID_CHILD"=TRUNC(:B2+1))))
3 - access("MM1"."ID_PARENT"=100 AND "MM1"."ID_CHILD"=1)
4 - filter("STATUS"=1)
5 - access("MM2"."ID_PARENT"=100)
filter("MM2"."ID_CHILD"=TRUNC(:B1) OR
"MM2"."ID_CHILD"=TRUNC(:B2+1))
Conclusion
原文链接:
原文作者:

最后修改时间:2020-02-26 12:52:59
文章转载自数据库平台优化,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。





