
在这系列的前两个部分中,我们列举了一些生成或者找回执行计划的方法,也学习了一些额外的步骤可以帮助我们模拟真实的环境来判断执行计划是否存在问题。这部分我们会熟悉整体解释执行计划的基本准则。在第四部分前,我们不用考虑使用谓词的部分。
The Shape of a plan
create table t1
as
with generator as (
select --+ materialize
rownum id
from dual
connect by
level <= 1e4
)
select
rownum id,
mod(rownum,1000) n_1000,
lpad(rownum,6,'0') v1,
rpad('x',100,'x') padding
from
generator
;
alter table t1 add constraint t1_pk primary key(id);
create index t1_i1 on t1(n_1000);
begin
dbms_stats.gather_table_stats(
ownname => user,
tabname =>'T1',
method_opt => 'for all columns size 1'
);
end;/
explain plan for
select
t1.v1,t2.v1
from
t1,t2
where
t1.n_1000= 1
andt2.id = t1.id
andt2.n_1000= 100
;
select * from table(dbms_xplan.display);
select
id,parent_id, position,
depth,level ? 1 old_depth,
rpad('',level - 1) ||
operation|| ' ' ||
lower(options)|| ' ' ||
object_name text_line
from
plan_table
start with
id= 0
connect by
parent_id= prior id
order siblings by
id,position
;
我用两种不同的方法生成执行计划---一种是对dbms_xplan.display()的调用,另一种是在9i或者更早版本种对于plan table的简化查询。
--------------------------------------------------------------------------------------
| Id | Operation |Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0| SELECT STATEMENT | | 10 | 300 | 22 (0)| 00:00:01 |
|* 1| HASH JOIN | | 10 | 300 | 22 (0)| 00:00:01 |
| 2| TABLE ACCESS BY INDEX ROWID| T1 | 10 | 150 | 11 (0)| 00:00:01 |
|* 3| INDEX RANGE SCAN | T1_I1 | 10 | | 1 (0)| 00:00:01 |
| 4| TABLE ACCESS BY INDEX ROWID| T2 | 10 | 150 | 11 (0)| 00:00:01 |
|* 5| INDEX RANGE SCAN | T2_I1 | 10 | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified byoperation id):
---------------------------------------------------
1- access("T2"."ID"="T1"."ID")
3- access("T1"."N_1000"=1)
5- access("T2"."N_1000"=100)
Id Par Pos DEPTH OLD_DEPTH TEXT_LINE
---- ---- ---- ----- -----------------------------------------------------------
0 22 0 0 SELECT STATEMENT
1 0 1 1 1 HASH JOIN
2 1 1 2 2 TABLE ACCESS by index rowid T1
3 2 1 3 3 INDEX range scan T1_I1
4 1 2 2 2 TABLE ACCESS by index rowid T2
5 4 1 3 3 INDEX range scan T2_I1
我之所以列出较早版本的获取执行计划的方法,是因为这样可以让你将调用dbms_xplan.display的结果的直观展示与plan table中的被隐藏的细节相关联。我们总是看到执行计划都是呈锯齿状,这种方式是为了告诉我们执行计划中操作的关系。也直观的展示id,parent_id和position列之间的关系。
根据id顺序列出的行总是能给我们正确计划的顺序(但是这并不是获取跟操作数据的顺序);而且计算在锯齿状中的行的方法在9i跟10g中也不一样。以前的版本中,来自分级connect by查询的派生列级别允许我们向文本添加适当大小的缩进---但是当Oracle允许我们从内存中获取执行计划的版本(v$sql_plan)时,这种方法就变得十分低效,因此,动态性能视图中包含了一个预先计算的level列(与level相差1,称为depth),其值在语句被优化时获得,并最终将其添加到plan表中(即使在12c中也有一些生成的值不正确的情况,所以记住怎么根据connect by查询获得执行计划是很有帮助的)。
First Rule for Reading Plans
--------------------------------------------------------------------------------------
| Id | Operation |Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 2| TABLE ACCESS BY INDEX ROWID| T1 | 10 | 150 | 11 (0)| 00:00:01 |
|* 3| INDEX RANGE SCAN | T1_I1 | 10 | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
从上面分离出来部分的执行计划可以看到,第2行有个单独的子操作进行调用,这个子操作是index range scan。我们可以很容易的看到,通过index range scan生成的rowsource可能包含rowid,而且第2行根据rowid进行表扫描---当试图理解更复杂的执行计划时,类似这样的完整性检查(将父级的需求与子级的提供的数据进行比较)非常有用。
--------------------------------------------------------------------------------------
| Id | Operation |Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 4| TABLE ACCESS BY INDEX ROWID| T2 | 10 | 150 | 11 (0)| 00:00:01 |
|* 5| INDEX RANGE SCAN | T2_I1 | 10 | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
同样这也是一个很简单的计划,我们调用第5行的操作进行index range scan,并且在第4行中根据第5行提供的rowid进行访问表。
------------------------------------------------------
| Id | Operation |Name | Order |
------------------------------------------------------
| 0| SELECT STATEMENT | | 6 |
|* 1| HASH JOIN | | 5 |
| 2| TABLE ACCESS BY INDEX ROWID| T1 | 2 |
|* 3| INDEX RANGE SCAN | T1_I1 | 1 |
| 4| TABLE ACCESS BY INDEX ROWID| T2 | 4 |
|* 5| INDEX RANGE SCAN | T2_I1 | 3 |
------------------------------------------------------
我们根据如下顺序解读执行计划:
计划中还有比这更多的内容——特别是我们需要更多地考虑操作的时机:有些是“批量”处理,有些是“单行”处理;我们需要引入谓词部分并考虑“访问”和“筛选器”的相关性;这些细节我们将在下一篇文章中看到。
Closing thoughts




