计划表
计划表是EXPLAIN
PLAN语句输出内容写入的地方。如计划表不存在,则会抛出一个错误。默认的计划表归SYS所有,一个名为plan_table的公共同义词将这张表暴露给所有的用户。一旦需要一张私有的计划表,通过utlxplan.sql脚本手工创建是一个不错的实践,脚本可以在$ORACLE_HOME/rdbms/admin目录下找到。如果计划表是手工创建的,一旦执行了数据库升级,不要忘记将计划表删掉并重新创建。实际上,这往往会发生在新版本添加了新属性的时候。
有趣的是,默认的计划表是一张会将数据存储直到会话结束的全局临时表(它是一张使用on
commit preserve rows选项创建的全局临时表。)。通过这种方式,几个并发的用户可以同时使用它而不互相干扰。
要将计划表与EXPLAIN
PLAN语句一起使用,至少需要INSERT和select权限。尽管可以不使用DELETE权限执行基本的操作,但最好还是授予该权限。
在这里我不会完整地描述计划表,原因很简单:你通常不会直接查询它。关于这张表的列的详细描述,请参考Performance Tuning Guide(11.2及之前的版本),或SQL Tuning
Guide(从12.1版本开始)。
查询计划表
很显然,可以直接通过针对计划表发起查询来获取执行计划。但是,使用dbms_xplan包的display函数会简单得多,如接下来的例子所示。可以看到,它的使用非常简单。实际上,为了显示EXPLAIN PLAN语句生成的执行计划,调用这个函数就足够了。注意这个函数返回的值(即一个结果集)是如何通过table函数转换的:
explain plan for select * from emp where
deptno = 10 order by ename;
select * from table(dbms_xplan.display);
display函数不仅仅限于不带参数的用法。基于这个原因,本章稍后会介绍dbms_xplan包,探索所有的可能性,包括对产生的输出的描述。
绑定变量陷阱
我遇到过的使用EXPLAIN
PLAN语句最常见的错误是,指定了一个有别于要分析的语句的SQL语句。当然,那会导致错误的执行计划。因为格式本身对执行计划没有影响,差别通常由替换绑定变量引起。来检查一下接下来的PL/SQL存储过程中查询语句使用的执行计划:
CREATE OR REPLACE PROCEDURE p(p_value IN
NUMBER) IS
BEGIN
FOR
i IN (select * FROM emp WHERE empno = p_value)
LOOP
NULL;-- do something
END
LOOP;
END;
/
常用的技巧是使用字面值替换PL/SQL变量来复制/粘贴查询语句。执行类似这样的SQL语句:
explain plan for select * from emp where
empno = 7788;
问题是通过使用字面值替换绑定变量,你向查询优化器提交了一条不一样的SQL语句。这种改变可能会对查询优化器做出的决定产生影响。改变是因为SQL概要、存储纲要、SQL计划基线的存在,或者查询优化器用来估算在WHERE子句中使用的谓词选择率的方法(字面值和绑定变量不是按照相同的方式处理的)。
正确的途径是使用相同的SQL语句。这是可行的,因为绑定变量可以在EXPLAIN PLAN语句中使用。例如,你应该执行类似的SQL语句(注意,p_value PL/SQL变量被:B1绑定变量替换了,因为PL/SQL引擎也会这么做):
explain plan for select * from emp where
empno =:b1;
尽管如此,在EXPLAIN
PLAN语句中使用绑定变量有两个问题。第一个问题是,默认情况下,绑定变量会被以VARCHAR2类型声明。结果,数据库引擎可能会自动添加一个隐式转换,而那样做会改变执行计划。这点可以通过在dbms_xplan包中的display函数生成的输出的末尾显示的关于谓词的信息来检查。在下面的输出例子中,to_number函数被用于这个目的:
select * from table(dbms_xplan.display);
2 -
access("EMPNO"=TO_NUMBER(:B1))
通常,检查是否正确处理了数据类型是很好的做法,比如,通过为原始SQL语句中所有不是VARCHAR2类型的绑定变量使用显式转换。
第二个问题是在EXPLAIN
PLAN语句中使用绑定变量时不会使用绑定变量扫视技术。因为这个问题没有解决方案,所以不能保证通过EXPLAIN PLAN语句生成的执行计划就是运行时会选择的执行计划。换句话说,一旦涉及绑定变量,通过EXPLAIN PLAN语句生成的输出是靠不住的。




