评估器通过计算三个值来评估计划的总体成本:选择性(Selectivity)、基数(Cardinality)、成本(Cost)。
选择性:是一个大于0小于1的数,0表示没有记录被选定,1表示所有记录都被选定。统计信息和直方图关系到选择性值的准确性。如:name=’Davis’,如果不存在统计信息评估器将根据所用的谓词来指定一个缺省的选择性值,此时评估器会始终认为等式谓词的选择性比不等式谓词小;如果存在统计信息而不存在直方图,此时选择性值为1/count(distinct name);如果存在统计信息也存在直方图,选择性值则为count(name)where name=’Davis’ / count(name)where name is not null。
基数:通常表中的行数称为“基础基数”(Base cardinality);当用WHERE中的条件过滤后剩下的行数称为“有效基数”(Effective cardinality);连接操作之后产生的结果集行数称为“连接基数”(Join cardinality);一个字段DISTINCT之后的行数称为“DISTINCT基数”;“GROUP基数”(Group cardinality)比较特殊,它与基础基数和DISTINCT基数有关,例如:group by colx则GROUP基数就等于基础基数,但是group by colx,coly的GROUP基数则大于max ( distinct cardinality of colx , distinct cardinality of coly )且小于min ( (distinct cardinality of colx * distinct cardinality of coly) , base cardinality)。
成本:就是度量资源消耗的单位。可以理解为执行表扫描、索引扫描、连接、排序等操作所消耗I/O、CPU、内存的数量。
l RULE:意义同OPTIMIZER_MODE=RULE区别在于HINTS作用在语句级,10g中该HINTS已被废弃。
l CHOOSE:意义同OPTIMIZER_MODE=CHOOSE,10g中已被废弃。
l FIRST_ROWS:意义同OPTIMIZER_MODE=FIRST_ROWS,10g中已被废弃。
l ALL_ROWS:意义同OPTIMIZER_MODE=ALL_ROWS。
l FIRST_ROWS(n):意义同OPTIMIZER_MODE=FIRST_ROWS_n。
l CPU_COSTING:启用CPU成本计算,也就是在总成本中考虑CPU的成本,缺省是启用的。该HINTS是10g中新增加的。
l NO_CPU_COSTING:关闭CPU成本计算,也就是在总成本中不考虑CPU的成本,只计算I/O的成本。该HINTS也是10g中新增加的。
1)唯一索引扫描
在利用一个主键字段或含有唯一约束的字段选择一行记录时,通常发生唯一索引扫描。
2)索引范围扫描
索引范围扫描返回的数据返照索引字段值升序排列,值相同的按Rowid升序排列。如果在语句中使用了ORDER BY ASC子句,而且排序字段是索引字段时Oracle不会对ORDER BY再次排序。
例如:
SQL> select * from t;
COLX COLY
--------------- ---------------
1 3
1 2
1 1
1 0
SQL> create index ind_t on t(coly);
SQL> set autotrace on
SQL> select * from t where coly>0;
COLX COLY
--------------- ---------------
1 1
1 2
1 3
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T'
2 1 INDEX (RANGE SCAN) OF 'IND_T' (NON-UNIQUE)
没有使用ORDER BY结果集已经是按COLY升序排列。
SQL> set autotrace traceonly
SQL> select * from t where coly>0 order by coly;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T'
2 1 INDEX (RANGE SCAN) OF 'IND_T' (NON-UNIQUE)
例如:
SQL> select /*+index_desc(t ind_t)*/colx,coly from t where coly<3;
COLX COLY
--------------- ---------------
1 2
1 1
1 0
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=4 Bytes=104)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=1 Card=4 Bytes=104)
2 1 INDEX (RANGE SCAN DESCENDING) OF 'IND_T' (NON-UNIQUE) (Cost=2 Card=1)
4)跳跃式索引扫描
跳跃式索引扫描是用来提高复合索引效率的,通常当复合索引的第一个索引字段不在语句中指定时是无法使用复合索引的,此时如果复合索引的第一个索引字段DISTINCT值非常小,而复合索引的其他索引字段DISTINCT值非常大时,可以使用跳跃式索引扫描来跳过该复合索引的第一个索引字段。跳跃式扫描会使复合索引在逻辑上分裂成N个较小的索引,N值等于复合索引的第一个索引字段的ISTINCT值。
例如:
SQL> select* from employees;
SEX EMPLOYEE_ID ADDRESS
------ -------------------- --------------------
F 98 ABC
F 100 ABC
F 102 ABC
F 104 ABC
M 101 ABC
M 103 ABC
M 105 ABC
SQL> create index ind_sex_empid on employees(sex,employee_id);
SQL>set autotrace traceonly
SQL>select/*+index_ss(employees ind_sex_empid)*/* from employees where employee_id=101;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=1 Bytes=11)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'EMPLOYEES' (Cost=3 Card=1 Bytes=11)
2 1 INDEX (SKIP SCAN) OF 'IND_SEX_EMPID' (NON-UNIQUE) (Cost=2 Card=1)
5)全索引扫描
当查询涉及的字段都包含在索引中,如果WHERE子句中谓词非第一个索引字段,或无WHERE子句但是被索引字段中至少有一个非空属性时,通常会做全索引扫描。全索引扫描结果集按索引字段排序。
嵌套循环连接,在于外表返回的每一行都要在内表中进行匹配的成本,成本计算如下:
cost of nested loop = access cost of outer + (number of rows from outer * access cost of inner)
排序合并连接,在于把两个大表读入内存并进行排序的成本,成本计算如下:
cost of merge join = access cost of A + access cost of B + (sort cost of A + sort cost of B)
散列连接,在于将小表读入内存分成若干散列表,然后由大表对每个散列表都进行一次匹配的成本,成本计算如下:
cost of hash join = access cost of smaller + (access cost of bigger * number of hash partitions of smaller)
以上成本计算公式不是绝对的,优化器对成本的评估还会受到其他因素的影响,比如:内存排序区过小会增加排序合并连接的成本,由于此种情况下的排序消耗了过多的CPU和I/O。多块读取会降低排序合并连接的成本,如果内表的连接字段存在索引也会降低嵌套循环连接的成本。
嵌套循环连接,在于外表返回的每一行都要在内表中进行匹配的成本,成本计算如下:
cost of nested loop = access cost of outer + (number of rows from outer * access cost of inner)
排序合并连接,在于把两个大表读入内存并进行排序的成本,成本计算如下:
cost of merge join = access cost of A + access cost of B + (sort cost of A + sort cost of B)
散列连接,在于将小表读入内存分成若干散列表,然后由大表对每个散列表都进行一次匹配的成本,成本计算如下:
cost of hash join = access cost of smaller + (access cost of bigger * number of hash partitions of smaller)
以上成本计算公式不是绝对的,优化器对成本的评估还会受到其他因素的影响,比如:内存排序区过小会增加排序合并连接的成本,由于此种情况下的排序消耗了过多的CPU和I/O。多块读取会降低排序合并连接的成本,如果内表的连接字段存在索引也会降低嵌套循环连接的成本。