在之前的文章里写了Oracle里常见的执行计划,可以参考文章:,这篇文章里介绍的是其他的一些典型的执行计划。
1. AND-EQUAL(INDEX MERGE)
AND-EQUAL又称为INDEX MERGE,顾名思义,INDEX MERGE就是指如果where条件里出现了多个针对不同单列的等值条件,并且这些列上都有单键值的索引,则Oracle可能会以相应的单个等值条件去分别扫描这些索引;然后Oracle会合并这些扫描单个索引所得到的rowid集合,如果能从这些集合中找到相同的rowid,那么这个rowid就是目标SQL最终执行结果所对应的rowid。最后,Oracle只需要用这些rowid回表就能得到目标SQL的最终执行结果。
AND-EQUAL在执行计划中对应的关键字就是“AND-EQUAL”,我们可以使用Hint来强制让Oracle走AND-EQUAL。
看一个实例:
zx@MYDB>create table emp_temp as select * from scott.emp;
Table created.
zx@MYDB>create index idx_mgr on emp_temp(mgr);
Index created.
zx@MYDB>create index idx_deptno on emp_temp(deptno);
Index created.
zx@MYDB>select /*+ and_equal(emp_temp idx_mgr idx_deptno) */ empno,job from emp_temp where mgr=7902 and deptno=20;
EMPNO JOB
---------- ---------------------------
7369 CLERK
从上述显示内容中可以看出,现在此SQL的执行计划走的是对索引IDX_MGR和IDX_DEPTNO的AND-EQUAL。
2. INDEX JOIN
INDEX JOIN很容易引起误解,因为它并不是指通常意义上针对多表的表连接。这里INDEX JOIN指的是针对单表上的不同索引之间的连接。
还以上面的EMP_TEMP为例,已经在列MGR和DEPTNO上分别创建了两个单键值的B*Tree索引,如果此时执行SQL语句“select mgr,deptno from emp_temp”,因为这里要查询的列MGR和DEPTNO均可来源于索引IDX_MGR和IDX_DEPTNO(不考虑NULL值),不用回表,所以除了常规的执行方法之外,Oracle还可以采用如下方法:分别扫描索引IDX_MGR和IDX_DEPTNO,得到的结果集分别记为结果集1和结果集2,然后将结果集1和2做一个连接,连接条件就是“结果集1.rowid=结果集2.rowid”,这样得到的最终连接结果(不用回表)就是上述SQL的执行结果。
很显然,针对上述SQL的INDEX JOIN的执行效率是不如我们直接在列MGR和DEPTNO上建一个组合索引,然后直接扫描该组全索引的效率高。INDEX JOIN只是为CBO提供了一种可选的执行路径,大多数情况下,它只是额外多出的一种选择而已。
看一下例子:
zx@MYDB>delete from emp_temp where mgr is null;
1 row deleted.
zx@MYDB>commit;
Commit complete.
zx@MYDB>alter table emp_temp modify mgr not null;
Table altered.
zx@MYDB>alter table emp_temp modify deptno not null;
Table altered.
zx@MYDB>select mgr,deptno from emp_temp;
MGR DEPTNO
---------- ----------
7839 10
......
7698 30
13 rows selected.
从上述显示内容可以看出,现在目标SQL的执行计划走的是对索引IDX_MGR和IDX_DEPTNO的HASH JOIN。
3. VIEW
Oracle在处理包含视图的SQL时,根据该视图是否能做为视图合并(View Merging),其对应的执行计划有如下两种形式。
如果可以做视图合并,则Oracle在执行该SQL时可以直接针对该视图的基表,此时SQL的执行计划中很可能不会出现关键字“VIEW”(不能完全依赖关键字“VIEW”的出现与否来判断Oracle是否做了视图合并,因为对于某些SQL而言,即使Oracle已经做了视图合并但其所对应的执行计划中可能还会显示关键字“VIEW”)。
如果不能做视图合并,则Oracle将把该视图看作一个整体并独立地执行它,此时SQL的执行计划中将会出现关键字“VIEW”。
看一个实例,还是使用上面的EMP_TEMP表:
zx@MYDB>create view emp_mgr_view as select * from emp_temp where job='MANAGER';
View created.
zx@MYDB>select empno,sal from emp_mgr_view where ename='CLARK';
EMPNO SAL
---------- ----------
7782 2450