Oracle执行计划 讲解(一)
看懂Oracle执行计划是优化的第一步,让我们从下面的例子开始吧。
下面为补充内容
1、创建测试表
-
SQL>
create
table t
as
select
1
id,object_name
from dba_objects;
-
-
Table created
-
-
SQL>
update t
set
id=
99
where
rownum=
1;
-
-
1 row updated
-
-
SQL>
commit;
-
-
Commit
complete
-
-
SQL>
create
index t_ind
on t(
id);
-
-
Index created
-
oracle优化器:RBO和CBO两种, 从oracle10g开始优化器已经抛弃了RBO,下面的列子说明CBO大概是怎样的
-
SQL>
select
/*+dynamic_sampling(t 0) */*
from t
where
id=
1;
-
-
50819 rows selected.
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 1376202287
-
-
-------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-------------------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
195 |
15405 |
51 (
0)|
00:
00:
01 |
-
|
1 |
TABLE
ACCESS
BY
INDEX
ROWID| T |
195 |
15405 |
51 (
0)|
00:
00:
01 |
-
|*
2 |
INDEX
RANGE
SCAN | T_IND |
78 | |
50 (
0)|
00:
00:
01 |
-
-------------------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
2 -
access(
"ID"=
1)
现象t表还没有被分析,提示/*+dynamic_sampling(t 0) */*的目的是让CBO无法通过动态采样获取表中的实际数据情况,此时CBO只能根据T表中非常有限的信息(比如表中的extents数量,数据块的数量)来猜测表中的数据。从结果中可以看到CBO猜出表中id=1的有195条,这个数值对于表的总数来说,是一个非常小的值,所以CBO选择了索引而不是全表扫描。
而实际情况如下所示:
-
SQL>
select *
from t
where
id=
1
-
2 ;
-
-
50819 rows selected.
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 1601196873
-
-
--------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
--------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
49454 |
3815K|
67 (
2)|
00:
00:
01 |
-
|*
1 |
TABLE
ACCESS
FULL| T |
49454 |
3815K|
67 (
2)|
00:
00:
01 |
-
--------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
1 - filter(
"ID"=
1)
通过动态取样,CBO估算出行数为49454,非常接近于真实50820数目。选择了全表扫描。
我们来收集一下统计信息
-
SQL> exec dbms_stats.gather_table_stats(user,'t',cascade => true);
-
-
SQL>
select *
from t
where
id=
1;
-
-
50819 rows selected.
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 1601196873
-
-
--------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
--------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
50815 |
1339K|
67 (
2)|
00:
00:
01 |
-
|*
1 |
TABLE
ACCESS
FULL| T |
50815 |
1339K|
67 (
2)|
00:
00:
01 |
-
--------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
1 - filter(
"ID"=
1)
现在扫描过的行数为50815。
如果我们更新了所有的id为99看看。
-
SQL>
update t
set
id=
99;
-
-
50820 rows updated
-
-
SQL>
select *
from t
where
id=
99;
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 1376202287
-
-
-------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-------------------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
1 |
27 |
2 (
0)|
00:
00:
01 |
-
|
1 |
TABLE
ACCESS
BY
INDEX
ROWID| T |
1 |
27 |
2 (
0)|
00:
00:
01 |
-
|*
2 |
INDEX
RANGE
SCAN | T_IND |
1 | |
1 (
0)|
00:
00:
01 |
-
-------------------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
2 -
access(
"ID"=
99)
因为没有对表进行分析,所以表中的分析数据还是之前的信息,CBO并不知道。我们可以看出Rows值为1,也就是说CBO人为表T中的ID=99的值只有1条,所有选择仍然是索引。
我们收集一把统计信息。
-
SQL> exec dbms_stats.gather_table_stats(user,'t',cascade => true);
-
-
PL/SQL procedure successfully completed
-
-
SQL>
select *
from t
where
id=
99;
-
-
50820 rows selected.
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 1601196873
-
-
--------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
--------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
50815 |
1339K|
67 (
2)|
00:
00:
01 |
-
|*
1 |
TABLE
ACCESS
FULL| T |
50815 |
1339K|
67 (
2)|
00:
00:
01 |
-
--------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
1 - filter(
"ID"=
99)
上面为补充内容,下面正式开始
1、 sql的执行计划
创建测试表
-
SQL>
create
table t1(
id
int,
name varchar2(
1000));
-
-
Table created
-
-
SQL>
create
table t2(
id
int,
name varchar2(
1000));
-
-
Table created
-
-
SQL>
create
index ind_t1
on t1(
id);
-
-
Index created
-
-
SQL>
create
index ind_t2
on t2(
id);
-
-
Index created
-
-
SQL>
create
index ind_t2_name
on t2(
name);
-
-
Index created
-
-
SQL>
insert
into t1
select a.OBJECT_ID,a.OBJECT_NAME
from all_objects a;
-
-
50206 rows inserted
-
-
SQL>
insert
into t2
select a.OBJECT_ID,a.OBJECT_NAME
from all_objects a
where
rownum<=
20;
-
-
20 rows inserted
-
-
SQL>
commit;
-
-
Commit
complete
-
-
SQL> exec dbms_stats.gather_table_stats(
user,
't1',
cascade =>
true);
-
-
PL/SQL procedure successfully completed
-
-
SQL> exec dbms_stats.gather_table_stats(user,'t2',cascade => true);
-
-
PL/SQL procedure successfully completed
2、产生执行计划
-
SQL>
select *
from t1,t2
where t1.id= t2.id;
-
-
20 rows selected.
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 828990364
-
-
--------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
--------------------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
20 |
780 |
43 (
0)|
00:
00:
01 |
-
|
1 |
TABLE
ACCESS
BY
INDEX
ROWID| T1 |
1 |
28 |
2 (
0)|
00:
00:
01 |
-
|
2 |
NESTED LOOPS | |
20 |
780 |
43 (
0)|
00:
00:
01 |
-
|
3 |
TABLE
ACCESS
FULL | T2 |
20 |
220 |
3 (
0)|
00:
00:
01 |
-
|*
4 |
INDEX
RANGE
SCAN | IND_T1 |
1 | |
1 (
0)|
00:
00:
01 |
-
--------------------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
4 -
access(
"T1".
"ID"=
"T2".
"ID")
-
-
-
Statistics
-
----------------------------------------------------------
-
1
recursive calls
-
0 db
block gets
-
37
consistent gets
-
0
physical
reads
-
0
redo
size
-
1452
bytes sent via
SQL*Net
to
client
-
503
bytes received via
SQL*Net
from
client
-
3
SQL*Net roundtrips
to/
from
client
-
0 sorts (
memory)
-
0 sorts (disk)
-
20
rows processed
看执行计划时,我们首先从缩进最大的行读取,它是最先被执行的步骤。在执行计划中:id=3和id=4是最先被执行的,
两行缩进一样的,最上面的最先被执行,在这里就是id=3
| 3 | TABLE ACCESS FULL | T2 | 20 | 220 | 3 (0)| 00:00:01 | |* 4 | INDEX RANGE SCAN | IND_T1 | 1 | | 1 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL | T2 | 20 | 220 | 3 (0)| 00:00:01 |
选择次之缩进的行数id=2,表连接方式为NESTED LOOPS。
| 2 | NESTED LOOPS | | 20 | 780 | 43 (0)| 00:00:01 |
然后是id=1,扫描表的方式为TABLE ACCESS BY INDEX ROWID
| 1 | TABLE ACCESS BY INDEX ROWID| T1 | 1 | 28 | 2 (0)| 00:00:01 |
最后是id=0
| 0 | SELECT STATEMENT | | 20 | 780 | 43 (0)| 00:00:01 |
我们翻译成语言大概如下,
从t2表第一行读取,查看每一行是否符合下面条件:
"T1"."ID"="T2"."ID"
如果符合就拿出一行来,扫描整个t2表,这个过程就叫NESTED LOOPS
当整个t2表被扫描完之后,会产生一个结果集,这个结果集是IND_T1的一个索引集,然后oracle根据索引键值上的rowid去T1表中找到相应的记录,就是这一步:TABLE ACCESS BY INDEX ROWID
然后将结果返回:SELECT STATEMENT
id列为: id=3->id=4->id=2->id=1->id=0
让我们再看一看表中每一行表示什么含义:
1)Operation 列:当前操作的内容。
2)Rows 列 :就是当前操作的 cardinality ,Oracle估算当前操作的返回结果集。
3)Cost (%CPU) : Oracle计算出来的一个数值(代价),用于说明sql执行的代价。
4)Time 列:Oracle估算当前操作的时间。
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("T1"."ID"="T2"."ID")
这里有access和filter区别,access就表示这个谓词的条件的值将会影响数据的访问路径(一般针对索引),filter只起过滤作用。
举个例子
-
SQL>
select *
from t1
where t1.name=
'AA';
-
-
no rows selected
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 3617692013
-
-
--------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
--------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
2 |
56 |
69 (
2)|
00:
00:
01 |
-
|*
1 |
TABLE
ACCESS
FULL| T1 |
2 |
56 |
69 (
2)|
00:
00:
01 |
-
--------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
1 - filter(
"T1".
"NAME"=
'AA')
懂了吧。
下面我们来仔细分析Operation里面的内容
a、表访问方式
1.Full Table Scan (FTS) 全表扫描
In a FTS operation, the whole table is read up to the high water mark (HWM). The HWM marks the last block in the table that has ever had data written to it. If you have deleted all the rows then you will still read up to the HWM. Truncate resets the HWM back to the start of the table. FTS uses multiblock i/o to read the blocks from disk. --全表扫描模式下会读数据到表的高水位线(HWM即表示表曾经扩展的最后一个数据块),读取速度依赖于Oracle初始化参db_block_multiblock_read_count(我觉得应该这样翻译:FTS扫描会使表使用上升到高水位(HWM),HWM标识了表最后写入数据的块,如果你用DELETE删除了所有的数据表仍然处于高水位(HWM),只有用TRUNCATE才能使表回归,FTS使用多IO从磁盘读取数据块).
Query Plan------------------------------------SELECT STATEMENT [CHOOSE] Cost=1**INDEX UNIQUE SCAN EMP_I1 --如果索引里就找到了所要的数据,就不会再去访问表
2.Index Lookup 索引扫描There are 5 methods of index lookup:
1)index unique scan --索引唯一扫描Method for looking up a single key value via a unique index. always returns a single value, You must supply AT LEAST the leading column of the index to access data via the index.eg:SQL> select empno,ename from emp where empno=10;
-
SQL>
select empno,ename
from emp
where empno=
10;
-
-
no rows selected
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 2949544139
-
-
--------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
--------------------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
1 |
20 |
1 (
0)|
00:
00:
01 |
-
|
1 |
TABLE
ACCESS
BY
INDEX
ROWID| EMP |
1 |
20 |
1 (
0)|
00:
00:
01 |
-
|*
2 |
INDEX
UNIQUE
SCAN | PK_EMP |
1 | |
1 (
0)|
00:
00:
01 |
-
--------------------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
2 -
access(
"EMPNO"=
10)
-
-
-
Statistics
-
----------------------------------------------------------
-
24
recursive calls
-
0 db
block gets
-
3
consistent gets
-
0
physical
reads
-
0
redo
size
-
385
bytes sent via
SQL*Net
to
client
-
481
bytes received via
SQL*Net
from
client
-
1
SQL*Net roundtrips
to/
from
client
-
0 sorts (
memory)
-
0 sorts (disk)
-
0
rows processed
2)index range scan --索引局部扫描
Index range scan is a method for accessing a range values of a particular column. AT LEAST the leading column of the index must be supplied to access data via the index. Can be used for range operations (e.g. > < <> >= <= between) .
-
SQL>
select empno
from emp
where EMPNO>=
7902;
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 1567865628
-
-
---------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
---------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
2 |
26 |
2 (
0)|
00:
00:
01 |
-
|*
1 |
INDEX
RANGE
SCAN| PK_EMP |
2 |
26 |
2 (
0)|
00:
00:
01 |
-
---------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
1 -
access(
"EMPNO">=
7902)
-
-
Note
-
-----
-
- dynamic sampling used
for this
statement
-
-
-
Statistics
-
----------------------------------------------------------
-
0
recursive calls
-
0 db
block gets
-
2
consistent gets
-
0
physical
reads
-
0
redo
size
-
569
bytes sent via
SQL*Net
to
client
-
492
bytes received via
SQL*Net
from
client
-
2
SQL*Net roundtrips
to/
from
client
-
0 sorts (
memory)
-
0 sorts (disk)
-
2
rows processed
3)index full scan --索引全局扫描
Full index scans are only available in the CBO as otherwise we are unable to determine whether a full scan would be a good idea or not. We choose an index Full Scan when we have statistics that indicate that it is going to be more efficient than a Full table scan and a sort. For example we may do a Full index scan when we do an unbounded scan of an index and want the data to be ordered in the index order.
-
SQL>
select empno
from emp
order
by empno;
-
-
14 rows selected.
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 179099197
-
-
---------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
---------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
14 |
182 |
2 (
0)|
00:
00:
01 |
-
|
1 |
INDEX
FULL
SCAN | PK_EMP |
14 |
182 |
2 (
0)|
00:
00:
01 |
-
---------------------------------------------------------------------------
-
-
Note
-
-----
-
- dynamic sampling used
for this
statement
-
-
-
Statistics
-
----------------------------------------------------------
-
4
recursive calls
-
0 db
block gets
-
11
consistent gets
-
0
physical
reads
-
0
redo
size
-
676
bytes sent via
SQL*Net
to
client
-
492
bytes received via
SQL*Net
from
client
-
2
SQL*Net roundtrips
to/
from
client
-
0 sorts (
memory)
-
0 sorts (disk)
-
14
rows processed
4)index fast full scan --索引快速全局扫描,不带order by情况下常发生
Scans all the block in the index, Rows are not returned in sorted order, Introduced in 7.3 and requires V733_PLANS_ENABLED=TRUE and CBO, may be hinted using INDEX_FFS hint, uses multiblock i/o, can be executed in parallel, can be used to access second column of concatenated indexes. This is because we are selecting all of the index.
-
SQL>
select empno
from emp;
-
-
14 rows selected.
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 366039554
-
-
-------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-------------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
14 |
182 |
2 (
0)|
00:
00:
01 |
-
|
1 |
INDEX
FAST
FULL
SCAN| PK_EMP |
14 |
182 |
2 (
0)|
00:
00:
01 |
-
-------------------------------------------------------------------------------
-
-
Note
-
-----
-
- dynamic sampling used
for this
statement
-
-
-
Statistics
-
----------------------------------------------------------
-
4
recursive calls
-
0 db
block gets
-
13
consistent gets
-
0
physical
reads
-
0
redo
size
-
676
bytes sent via
SQL*Net
to
client
-
492
bytes received via
SQL*Net
from
client
-
2
SQL*Net roundtrips
to/
from
client
-
0 sorts (
memory)
-
0 sorts (disk)
-
14
rows processed
5)index skip scan --索引跳跃扫描,where条件列是非索引的前导列情况下常发生
Index skip scan finds rows even if the column is not the leading column of a concatenated index. It skips the first column(s) during the search.
-
SQL>
create
index i_emp
on emp(empno, ename);
-
-
Index created.
-
SQL>
select
/*+ index_ss(emp i_emp)*/ job
from emp
where ename=
'SMITH';
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 98078853
-
-
-------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-------------------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
1 |
13 |
5 (
0)|
00:
00:
01 |
-
|
1 |
TABLE
ACCESS
BY
INDEX
ROWID| EMP |
1 |
13 |
5 (
0)|
00:
00:
01 |
-
|*
2 |
INDEX
SKIP
SCAN | I_EMP |
1 | |
4 (
0)|
00:
00:
01 |
-
-------------------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
2 -
access(
"ENAME"=
'SMITH')
-
filter(
"ENAME"=
'SMITH')
-
-
Note
-
-----
-
- dynamic sampling used
for this
statement
-
-
-
Statistics
-
----------------------------------------------------------
-
5
recursive calls
-
0 db
block gets
-
11
consistent gets
-
0
physical
reads
-
0
redo
size
-
513
bytes sent via
SQL*Net
to
client
-
492
bytes received via
SQL*Net
from
client
-
2
SQL*Net roundtrips
to/
from
client
-
0 sorts (
memory)
-
0 sorts (disk)
-
1
rows processed
3.Rowid 物理ID扫描
This is the quickest access method available.Oracle retrieves the specified block and extracts the rows it is interested in.
--Rowid扫描是最快的访问数据方式
-
SQL>
select *
from emp
where
rowid=
'AAAjFUAAEAAABZ1AAM';
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 1116584662
-
-
-----------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-----------------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
1 |
99 |
1 (
0)|
00:
00:
01 |
-
|
1 |
TABLE
ACCESS
BY
USER
ROWID| EMP |
1 |
99 |
1 (
0)|
00:
00:
01 |
-
-----------------------------------------------------------------------------------
-
-
-
Statistics
-
----------------------------------------------------------
-
1
recursive calls
-
0 db
block gets
-
1
consistent gets
-
0
physical
reads
-
0
redo
size
-
983
bytes sent via
SQL*Net
to
client
-
492
bytes received via
SQL*Net
from
client
-
2
SQL*Net roundtrips
to/
from
client
-
0 sorts (
memory)
-
0 sorts (disk)
-
1
rows processed
-
b、运算符
1.sort --排序,很消耗资源
There are a number of different operations that promote sorts:
(1)order by clauses (2)group by (3)sort merge join –-这三个会产生排序运算
2.filter --过滤,如not in、min函数等容易产生
Has a number of different meanings, used to indicate partition elimination, may also indicate an actual filter step where one row source is filtering, another, functions such as min may introduce filter steps into query plans.
3.view --视图,大都由内联视图产生(可能深入到视图基表)
When a view cannot be merged into the main query you will often see a projection view operation. This indicates that the 'view' will be selected from directly as opposed to being broken down into joins on the base tables. A number of constructs make a view non mergeable. Inline views are also non mergeable.
-
SQL>
select ename,tot
from emp,(
select empno,
sum(empno) tot
from emp
group
by empno) tmp
where emp.empno = tmp.empno;
-
-
14 rows selected.
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 138960760
-
-
-----------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-----------------------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
14 |
644 |
4 (
25)|
00:
00:
01 |
-
|
1 |
MERGE
JOIN | |
14 |
644 |
4 (
25)|
00:
00:
01 |
-
|
2 |
TABLE
ACCESS
BY
INDEX
ROWID| EMP |
14 |
280 |
2 (
0)|
00:
00:
01 |
-
|
3 |
INDEX
FULL
SCAN | PK_EMPNO |
14 | |
1 (
0)|
00:
00:
01 |
-
|*
4 |
SORT
JOIN | |
14 |
364 |
2 (
50)|
00:
00:
01 |
-
|
5 |
VIEW | |
14 |
364 |
1 (
0)|
00:
00:
01 |
-
|
6 |
HASH
GROUP
BY | |
14 |
182 |
1 (
0)|
00:
00:
01 |
-
|
7 |
INDEX
FULL
SCAN | PK_EMPNO |
14 |
182 |
1 (
0)|
00:
00:
01 |
-
-----------------------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
4 -
access(
"EMP".
"EMPNO"=
"TMP".
"EMPNO")
-
filter(
"EMP".
"EMPNO"=
"TMP".
"EMPNO")
-
-
Note
-
-----
-
- dynamic sampling used
for this
statement
-
-
-
Statistics
-
----------------------------------------------------------
-
43
recursive calls
-
0 db
block gets
-
61
consistent gets
-
0
physical
reads
-
0
redo
size
-
821
bytes sent via
SQL*Net
to
client
-
492
bytes received via
SQL*Net
from
client
-
2
SQL*Net roundtrips
to/
from
client
-
5 sorts (
memory)
-
0 sorts (disk)
-
14
rows processed
4.partition view --分区视图
Partition views are a legacy technology that were superceded by the partitioning option. This section of the article is provided as reference for such legacy systems.
3、让我们再看看统计信息部分
-
SQL>
set autotrace traceonly;
-
SQL>
select
count(*)
from emp;
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 2083865914
-
-
-------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-
-------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
1 |
3 (
0)|
00:
00:
01 |
-
|
1 |
SORT
AGGREGATE | |
1 | | |
-
|
2 |
TABLE
ACCESS
FULL| EMP |
14 |
3 (
0)|
00:
00:
01 |
-
-------------------------------------------------------------------
-
-
Note
-
-----
-
- dynamic sampling used
for this
statement
-
-
Statistics
-
----------------------------------------------------------
-
5
recursive calls (归调用次数)
-
0 db
block gets (从磁盘上读取的块数,即通过
update/
delete/
select
for
update读的次数)
-
15
consistent gets (从内存里读取的块数,即通过不带
for
update的
select 读的次数)
-
0
physical
reads (物理读—从磁盘读到数据块数量,一般来说是
'consistent gets' +
'db block gets')
-
0
redo
size (重做数——执行
SQL的过程中,产生的重做日志的大小)
-
515
bytes sent via
SQL*Net
to
client
-
492
bytes received via
SQL*Net
from
client
-
2
SQL*Net roundtrips
to/
from
client
-
0 sorts (
memory) (在内存中发生的排序)
-
0 sorts (disk) (在硬盘中发生的排序)
-
1
rows processed
说明:
Cost=(Single block I/O cost+ Multiblock I/O cost+ CPU cost)/sreadtim
序号 |
列名 |
解释 |
1 |
db block gets |
从buffer cache中读取的block的数量 |
2 |
consistent gets |
从buffer cache中读取的undo数据的block的数量 |
3 |
physical reads |
从磁盘读取的block的数量 |
4 |
redo size |
DML生成的redo的大小 |
5 |
sorts (memory) |
在内存执行的排序量 |
6 |
sorts (disk) |
在磁盘上执行的排序量 |
看懂Oracle执行计划是优化的第一步,让我们从下面的例子开始吧。
下面为补充内容
1、创建测试表
-
SQL>
create
table t
as
select
1
id,object_name
from dba_objects;
-
-
Table created
-
-
SQL>
update t
set
id=
99
where
rownum=
1;
-
-
1 row updated
-
-
SQL>
commit;
-
-
Commit
complete
-
-
SQL>
create
index t_ind
on t(
id);
-
-
Index created
-
oracle优化器:RBO和CBO两种, 从oracle10g开始优化器已经抛弃了RBO,下面的列子说明CBO大概是怎样的
-
SQL>
select
/*+dynamic_sampling(t 0) */*
from t
where
id=
1;
-
-
50819 rows selected.
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 1376202287
-
-
-------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-------------------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
195 |
15405 |
51 (
0)|
00:
00:
01 |
-
|
1 |
TABLE
ACCESS
BY
INDEX
ROWID| T |
195 |
15405 |
51 (
0)|
00:
00:
01 |
-
|*
2 |
INDEX
RANGE
SCAN | T_IND |
78 | |
50 (
0)|
00:
00:
01 |
-
-------------------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
2 -
access(
"ID"=
1)
现象t表还没有被分析,提示/*+dynamic_sampling(t 0) */*的目的是让CBO无法通过动态采样获取表中的实际数据情况,此时CBO只能根据T表中非常有限的信息(比如表中的extents数量,数据块的数量)来猜测表中的数据。从结果中可以看到CBO猜出表中id=1的有195条,这个数值对于表的总数来说,是一个非常小的值,所以CBO选择了索引而不是全表扫描。
而实际情况如下所示:
-
SQL>
select *
from t
where
id=
1
-
2 ;
-
-
50819 rows selected.
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 1601196873
-
-
--------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
--------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
49454 |
3815K|
67 (
2)|
00:
00:
01 |
-
|*
1 |
TABLE
ACCESS
FULL| T |
49454 |
3815K|
67 (
2)|
00:
00:
01 |
-
--------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
1 - filter(
"ID"=
1)
通过动态取样,CBO估算出行数为49454,非常接近于真实50820数目。选择了全表扫描。
我们来收集一下统计信息
-
SQL> exec dbms_stats.gather_table_stats(user,'t',cascade => true);
-
-
SQL>
select *
from t
where
id=
1;
-
-
50819 rows selected.
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 1601196873
-
-
--------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
--------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
50815 |
1339K|
67 (
2)|
00:
00:
01 |
-
|*
1 |
TABLE
ACCESS
FULL| T |
50815 |
1339K|
67 (
2)|
00:
00:
01 |
-
--------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
1 - filter(
"ID"=
1)
现在扫描过的行数为50815。
如果我们更新了所有的id为99看看。
-
SQL>
update t
set
id=
99;
-
-
50820 rows updated
-
-
SQL>
select *
from t
where
id=
99;
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 1376202287
-
-
-------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-------------------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
1 |
27 |
2 (
0)|
00:
00:
01 |
-
|
1 |
TABLE
ACCESS
BY
INDEX
ROWID| T |
1 |
27 |
2 (
0)|
00:
00:
01 |
-
|*
2 |
INDEX
RANGE
SCAN | T_IND |
1 | |
1 (
0)|
00:
00:
01 |
-
-------------------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
2 -
access(
"ID"=
99)
因为没有对表进行分析,所以表中的分析数据还是之前的信息,CBO并不知道。我们可以看出Rows值为1,也就是说CBO人为表T中的ID=99的值只有1条,所有选择仍然是索引。
我们收集一把统计信息。
-
SQL> exec dbms_stats.gather_table_stats(user,'t',cascade => true);
-
-
PL/SQL procedure successfully completed
-
-
SQL>
select *
from t
where
id=
99;
-
-
50820 rows selected.
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 1601196873
-
-
--------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
--------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
50815 |
1339K|
67 (
2)|
00:
00:
01 |
-
|*
1 |
TABLE
ACCESS
FULL| T |
50815 |
1339K|
67 (
2)|
00:
00:
01 |
-
--------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
1 - filter(
"ID"=
99)
上面为补充内容,下面正式开始
1、 sql的执行计划
创建测试表
-
SQL>
create
table t1(
id
int,
name varchar2(
1000));
-
-
Table created
-
-
SQL>
create
table t2(
id
int,
name varchar2(
1000));
-
-
Table created
-
-
SQL>
create
index ind_t1
on t1(
id);
-
-
Index created
-
-
SQL>
create
index ind_t2
on t2(
id);
-
-
Index created
-
-
SQL>
create
index ind_t2_name
on t2(
name);
-
-
Index created
-
-
SQL>
insert
into t1
select a.OBJECT_ID,a.OBJECT_NAME
from all_objects a;
-
-
50206 rows inserted
-
-
SQL>
insert
into t2
select a.OBJECT_ID,a.OBJECT_NAME
from all_objects a
where
rownum<=
20;
-
-
20 rows inserted
-
-
SQL>
commit;
-
-
Commit
complete
-
-
SQL> exec dbms_stats.gather_table_stats(
user,
't1',
cascade =>
true);
-
-
PL/SQL procedure successfully completed
-
-
SQL> exec dbms_stats.gather_table_stats(user,'t2',cascade => true);
-
-
PL/SQL procedure successfully completed
2、产生执行计划
-
SQL>
select *
from t1,t2
where t1.id= t2.id;
-
-
20 rows selected.
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 828990364
-
-
--------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
--------------------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
20 |
780 |
43 (
0)|
00:
00:
01 |
-
|
1 |
TABLE
ACCESS
BY
INDEX
ROWID| T1 |
1 |
28 |
2 (
0)|
00:
00:
01 |
-
|
2 |
NESTED LOOPS | |
20 |
780 |
43 (
0)|
00:
00:
01 |
-
|
3 |
TABLE
ACCESS
FULL | T2 |
20 |
220 |
3 (
0)|
00:
00:
01 |
-
|*
4 |
INDEX
RANGE
SCAN | IND_T1 |
1 | |
1 (
0)|
00:
00:
01 |
-
--------------------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
4 -
access(
"T1".
"ID"=
"T2".
"ID")
-
-
-
Statistics
-
----------------------------------------------------------
-
1
recursive calls
-
0 db
block gets
-
37
consistent gets
-
0
physical
reads
-
0
redo
size
-
1452
bytes sent via
SQL*Net
to
client
-
503
bytes received via
SQL*Net
from
client
-
3
SQL*Net roundtrips
to/
from
client
-
0 sorts (
memory)
-
0 sorts (disk)
-
20
rows processed
看执行计划时,我们首先从缩进最大的行读取,它是最先被执行的步骤。在执行计划中:id=3和id=4是最先被执行的,
两行缩进一样的,最上面的最先被执行,在这里就是id=3
| 3 | TABLE ACCESS FULL | T2 | 20 | 220 | 3 (0)| 00:00:01 | |* 4 | INDEX RANGE SCAN | IND_T1 | 1 | | 1 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL | T2 | 20 | 220 | 3 (0)| 00:00:01 |
选择次之缩进的行数id=2,表连接方式为NESTED LOOPS。
| 2 | NESTED LOOPS | | 20 | 780 | 43 (0)| 00:00:01 |
然后是id=1,扫描表的方式为TABLE ACCESS BY INDEX ROWID
| 1 | TABLE ACCESS BY INDEX ROWID| T1 | 1 | 28 | 2 (0)| 00:00:01 |
最后是id=0
| 0 | SELECT STATEMENT | | 20 | 780 | 43 (0)| 00:00:01 |
我们翻译成语言大概如下,
从t2表第一行读取,查看每一行是否符合下面条件:
"T1"."ID"="T2"."ID"
如果符合就拿出一行来,扫描整个t2表,这个过程就叫NESTED LOOPS
当整个t2表被扫描完之后,会产生一个结果集,这个结果集是IND_T1的一个索引集,然后oracle根据索引键值上的rowid去T1表中找到相应的记录,就是这一步:TABLE ACCESS BY INDEX ROWID
然后将结果返回:SELECT STATEMENT
id列为: id=3->id=4->id=2->id=1->id=0
让我们再看一看表中每一行表示什么含义:
1)Operation 列:当前操作的内容。
2)Rows 列 :就是当前操作的 cardinality ,Oracle估算当前操作的返回结果集。
3)Cost (%CPU) : Oracle计算出来的一个数值(代价),用于说明sql执行的代价。
4)Time 列:Oracle估算当前操作的时间。
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("T1"."ID"="T2"."ID")
这里有access和filter区别,access就表示这个谓词的条件的值将会影响数据的访问路径(一般针对索引),filter只起过滤作用。
举个例子
-
SQL>
select *
from t1
where t1.name=
'AA';
-
-
no rows selected
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 3617692013
-
-
--------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
--------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
2 |
56 |
69 (
2)|
00:
00:
01 |
-
|*
1 |
TABLE
ACCESS
FULL| T1 |
2 |
56 |
69 (
2)|
00:
00:
01 |
-
--------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
1 - filter(
"T1".
"NAME"=
'AA')
懂了吧。
下面我们来仔细分析Operation里面的内容
a、表访问方式
1.Full Table Scan (FTS) 全表扫描
In a FTS operation, the whole table is read up to the high water mark (HWM). The HWM marks the last block in the table that has ever had data written to it. If you have deleted all the rows then you will still read up to the HWM. Truncate resets the HWM back to the start of the table. FTS uses multiblock i/o to read the blocks from disk. --全表扫描模式下会读数据到表的高水位线(HWM即表示表曾经扩展的最后一个数据块),读取速度依赖于Oracle初始化参db_block_multiblock_read_count(我觉得应该这样翻译:FTS扫描会使表使用上升到高水位(HWM),HWM标识了表最后写入数据的块,如果你用DELETE删除了所有的数据表仍然处于高水位(HWM),只有用TRUNCATE才能使表回归,FTS使用多IO从磁盘读取数据块).
Query Plan------------------------------------SELECT STATEMENT [CHOOSE] Cost=1**INDEX UNIQUE SCAN EMP_I1 --如果索引里就找到了所要的数据,就不会再去访问表
2.Index Lookup 索引扫描There are 5 methods of index lookup:
1)index unique scan --索引唯一扫描Method for looking up a single key value via a unique index. always returns a single value, You must supply AT LEAST the leading column of the index to access data via the index.eg:SQL> select empno,ename from emp where empno=10;
-
SQL>
select empno,ename
from emp
where empno=
10;
-
-
no rows selected
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 2949544139
-
-
--------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
--------------------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
1 |
20 |
1 (
0)|
00:
00:
01 |
-
|
1 |
TABLE
ACCESS
BY
INDEX
ROWID| EMP |
1 |
20 |
1 (
0)|
00:
00:
01 |
-
|*
2 |
INDEX
UNIQUE
SCAN | PK_EMP |
1 | |
1 (
0)|
00:
00:
01 |
-
--------------------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
2 -
access(
"EMPNO"=
10)
-
-
-
Statistics
-
----------------------------------------------------------
-
24
recursive calls
-
0 db
block gets
-
3
consistent gets
-
0
physical
reads
-
0
redo
size
-
385
bytes sent via
SQL*Net
to
client
-
481
bytes received via
SQL*Net
from
client
-
1
SQL*Net roundtrips
to/
from
client
-
0 sorts (
memory)
-
0 sorts (disk)
-
0
rows processed
2)index range scan --索引局部扫描
Index range scan is a method for accessing a range values of a particular column. AT LEAST the leading column of the index must be supplied to access data via the index. Can be used for range operations (e.g. > < <> >= <= between) .
-
SQL>
select empno
from emp
where EMPNO>=
7902;
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 1567865628
-
-
---------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
---------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
2 |
26 |
2 (
0)|
00:
00:
01 |
-
|*
1 |
INDEX
RANGE
SCAN| PK_EMP |
2 |
26 |
2 (
0)|
00:
00:
01 |
-
---------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
1 -
access(
"EMPNO">=
7902)
-
-
Note
-
-----
-
- dynamic sampling used
for this
statement
-
-
-
Statistics
-
----------------------------------------------------------
-
0
recursive calls
-
0 db
block gets
-
2
consistent gets
-
0
physical
reads
-
0
redo
size
-
569
bytes sent via
SQL*Net
to
client
-
492
bytes received via
SQL*Net
from
client
-
2
SQL*Net roundtrips
to/
from
client
-
0 sorts (
memory)
-
0 sorts (disk)
-
2
rows processed
3)index full scan --索引全局扫描
Full index scans are only available in the CBO as otherwise we are unable to determine whether a full scan would be a good idea or not. We choose an index Full Scan when we have statistics that indicate that it is going to be more efficient than a Full table scan and a sort. For example we may do a Full index scan when we do an unbounded scan of an index and want the data to be ordered in the index order.
-
SQL>
select empno
from emp
order
by empno;
-
-
14 rows selected.
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 179099197
-
-
---------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
---------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
14 |
182 |
2 (
0)|
00:
00:
01 |
-
|
1 |
INDEX
FULL
SCAN | PK_EMP |
14 |
182 |
2 (
0)|
00:
00:
01 |
-
---------------------------------------------------------------------------
-
-
Note
-
-----
-
- dynamic sampling used
for this
statement
-
-
-
Statistics
-
----------------------------------------------------------
-
4
recursive calls
-
0 db
block gets
-
11
consistent gets
-
0
physical
reads
-
0
redo
size
-
676
bytes sent via
SQL*Net
to
client
-
492
bytes received via
SQL*Net
from
client
-
2
SQL*Net roundtrips
to/
from
client
-
0 sorts (
memory)
-
0 sorts (disk)
-
14
rows processed
4)index fast full scan --索引快速全局扫描,不带order by情况下常发生
Scans all the block in the index, Rows are not returned in sorted order, Introduced in 7.3 and requires V733_PLANS_ENABLED=TRUE and CBO, may be hinted using INDEX_FFS hint, uses multiblock i/o, can be executed in parallel, can be used to access second column of concatenated indexes. This is because we are selecting all of the index.
-
SQL>
select empno
from emp;
-
-
14 rows selected.
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 366039554
-
-
-------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-------------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
14 |
182 |
2 (
0)|
00:
00:
01 |
-
|
1 |
INDEX
FAST
FULL
SCAN| PK_EMP |
14 |
182 |
2 (
0)|
00:
00:
01 |
-
-------------------------------------------------------------------------------
-
-
Note
-
-----
-
- dynamic sampling used
for this
statement
-
-
-
Statistics
-
----------------------------------------------------------
-
4
recursive calls
-
0 db
block gets
-
13
consistent gets
-
0
physical
reads
-
0
redo
size
-
676
bytes sent via
SQL*Net
to
client
-
492
bytes received via
SQL*Net
from
client
-
2
SQL*Net roundtrips
to/
from
client
-
0 sorts (
memory)
-
0 sorts (disk)
-
14
rows processed
5)index skip scan --索引跳跃扫描,where条件列是非索引的前导列情况下常发生
Index skip scan finds rows even if the column is not the leading column of a concatenated index. It skips the first column(s) during the search.
-
SQL>
create
index i_emp
on emp(empno, ename);
-
-
Index created.
-
SQL>
select
/*+ index_ss(emp i_emp)*/ job
from emp
where ename=
'SMITH';
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 98078853
-
-
-------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-------------------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
1 |
13 |
5 (
0)|
00:
00:
01 |
-
|
1 |
TABLE
ACCESS
BY
INDEX
ROWID| EMP |
1 |
13 |
5 (
0)|
00:
00:
01 |
-
|*
2 |
INDEX
SKIP
SCAN | I_EMP |
1 | |
4 (
0)|
00:
00:
01 |
-
-------------------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
2 -
access(
"ENAME"=
'SMITH')
-
filter(
"ENAME"=
'SMITH')
-
-
Note
-
-----
-
- dynamic sampling used
for this
statement
-
-
-
Statistics
-
----------------------------------------------------------
-
5
recursive calls
-
0 db
block gets
-
11
consistent gets
-
0
physical
reads
-
0
redo
size
-
513
bytes sent via
SQL*Net
to
client
-
492
bytes received via
SQL*Net
from
client
-
2
SQL*Net roundtrips
to/
from
client
-
0 sorts (
memory)
-
0 sorts (disk)
-
1
rows processed
3.Rowid 物理ID扫描
This is the quickest access method available.Oracle retrieves the specified block and extracts the rows it is interested in.
--Rowid扫描是最快的访问数据方式
-
SQL>
select *
from emp
where
rowid=
'AAAjFUAAEAAABZ1AAM';
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 1116584662
-
-
-----------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-----------------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
1 |
99 |
1 (
0)|
00:
00:
01 |
-
|
1 |
TABLE
ACCESS
BY
USER
ROWID| EMP |
1 |
99 |
1 (
0)|
00:
00:
01 |
-
-----------------------------------------------------------------------------------
-
-
-
Statistics
-
----------------------------------------------------------
-
1
recursive calls
-
0 db
block gets
-
1
consistent gets
-
0
physical
reads
-
0
redo
size
-
983
bytes sent via
SQL*Net
to
client
-
492
bytes received via
SQL*Net
from
client
-
2
SQL*Net roundtrips
to/
from
client
-
0 sorts (
memory)
-
0 sorts (disk)
-
1
rows processed
-
b、运算符
1.sort --排序,很消耗资源
There are a number of different operations that promote sorts:
(1)order by clauses (2)group by (3)sort merge join –-这三个会产生排序运算
2.filter --过滤,如not in、min函数等容易产生
Has a number of different meanings, used to indicate partition elimination, may also indicate an actual filter step where one row source is filtering, another, functions such as min may introduce filter steps into query plans.
3.view --视图,大都由内联视图产生(可能深入到视图基表)
When a view cannot be merged into the main query you will often see a projection view operation. This indicates that the 'view' will be selected from directly as opposed to being broken down into joins on the base tables. A number of constructs make a view non mergeable. Inline views are also non mergeable.
-
SQL>
select ename,tot
from emp,(
select empno,
sum(empno) tot
from emp
group
by empno) tmp
where emp.empno = tmp.empno;
-
-
14 rows selected.
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 138960760
-
-
-----------------------------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-----------------------------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
14 |
644 |
4 (
25)|
00:
00:
01 |
-
|
1 |
MERGE
JOIN | |
14 |
644 |
4 (
25)|
00:
00:
01 |
-
|
2 |
TABLE
ACCESS
BY
INDEX
ROWID| EMP |
14 |
280 |
2 (
0)|
00:
00:
01 |
-
|
3 |
INDEX
FULL
SCAN | PK_EMPNO |
14 | |
1 (
0)|
00:
00:
01 |
-
|*
4 |
SORT
JOIN | |
14 |
364 |
2 (
50)|
00:
00:
01 |
-
|
5 |
VIEW | |
14 |
364 |
1 (
0)|
00:
00:
01 |
-
|
6 |
HASH
GROUP
BY | |
14 |
182 |
1 (
0)|
00:
00:
01 |
-
|
7 |
INDEX
FULL
SCAN | PK_EMPNO |
14 |
182 |
1 (
0)|
00:
00:
01 |
-
-----------------------------------------------------------------------------------------
-
-
Predicate Information (
identified
by operation
id):
-
---------------------------------------------------
-
-
4 -
access(
"EMP".
"EMPNO"=
"TMP".
"EMPNO")
-
filter(
"EMP".
"EMPNO"=
"TMP".
"EMPNO")
-
-
Note
-
-----
-
- dynamic sampling used
for this
statement
-
-
-
Statistics
-
----------------------------------------------------------
-
43
recursive calls
-
0 db
block gets
-
61
consistent gets
-
0
physical
reads
-
0
redo
size
-
821
bytes sent via
SQL*Net
to
client
-
492
bytes received via
SQL*Net
from
client
-
2
SQL*Net roundtrips
to/
from
client
-
5 sorts (
memory)
-
0 sorts (disk)
-
14
rows processed
4.partition view --分区视图
Partition views are a legacy technology that were superceded by the partitioning option. This section of the article is provided as reference for such legacy systems.
3、让我们再看看统计信息部分
-
SQL>
set autotrace traceonly;
-
SQL>
select
count(*)
from emp;
-
-
-
Execution Plan
-
----------------------------------------------------------
-
Plan hash value: 2083865914
-
-
-------------------------------------------------------------------
-
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-
-------------------------------------------------------------------
-
| 0 |
SELECT
STATEMENT | |
1 |
3 (
0)|
00:
00:
01 |
-
|
1 |
SORT
AGGREGATE | |
1 | | |
-
|
2 |
TABLE
ACCESS
FULL| EMP |
14 |
3 (
0)|
00:
00:
01 |
-
-------------------------------------------------------------------
-
-
Note
-
-----
-
- dynamic sampling used
for this
statement
-
-
Statistics
-
----------------------------------------------------------
-
5
recursive calls (归调用次数)
-
0 db
block gets (从磁盘上读取的块数,即通过
update/
delete/
select
for
update读的次数)
-
15
consistent gets (从内存里读取的块数,即通过不带
for
update的
select 读的次数)
-
0
physical
reads (物理读—从磁盘读到数据块数量,一般来说是
'consistent gets' +
'db block gets')
-
0
redo
size (重做数——执行
SQL的过程中,产生的重做日志的大小)
-
515
bytes sent via
SQL*Net
to
client
-
492
bytes received via
SQL*Net
from
client
-
2
SQL*Net roundtrips
to/
from
client
-
0 sorts (
memory) (在内存中发生的排序)
-
0 sorts (disk) (在硬盘中发生的排序)
-
1
rows processed
说明:
Cost=(Single block I/O cost+ Multiblock I/O cost+ CPU cost)/sreadtim
序号 |
列名 |
解释 |
1 |
db block gets |
从buffer cache中读取的block的数量 |
2 |
consistent gets |
从buffer cache中读取的undo数据的block的数量 |
3 |
physical reads |
从磁盘读取的block的数量 |
4 |
redo size |
DML生成的redo的大小 |
5 |
sorts (memory) |
在内存执行的排序量 |
6 |
sorts (disk) |
在磁盘上执行的排序量 |