1.1.1.1 修改存储概要
要更改存储概要名,需要执行ALTER
OUTLINE语句:
ALTER OUTLINE SYS_OUTLINE_13072411155434901
RENAME TO outline_from_sqlarea;
使用ALTER
OUTLINE语句或dbms_outln包下的update_by_cat过程,也可以修改存储概要的类别。然而前者修改单个存储概要的类别,后者把所有属于一个类别的存储概要都移动到另一个类别中。可是由于bug5759631,使用ALTER OUTLINE不能修改存储概要类别DEFAULT(对于其他类别,不存在这个问题)。下面的例子介绍了当你尝试修改时会发生什么,同时还介绍了如何使用update_by_cat过程执行同样的操作:
SQL> ALTER OUTLINE outline_from_text
CHANGE CATEGORY TO DEFAULT;
ALTER OUTLINE outline_from_text CHANGE
CATEGORY TO DEFAULT
ERROR
at line 1:
ORA-00931: missing identifier
SQL> execute
dbms_outln.update_by_cat(oldcat =>'TEST',newcat =>'DEFAULT');
select category FROM user_outlines WHERE
name='OUTLINE_FROM_TEXT';
最后,使用ALTER
OUTLINE语句,也可以生成存储概要,就像重建一样。通常情况下,会在想要查询优化器生成一组新的hint时使用该语句。如果更改了与存储概要相关的对象的访问结构,可能有必要使用该语句:
ALTER OUTLINE outline_from_text REBUILD;
1.1.1.2 激活存储概要
只有在存储概要被激活后查询优化器才会处理。要激活它,存储概要需要满足两个条件。第一,存储概要必须是启用的。在创建存储概要时,默认是启用的。要启用和停用存储概要,可以使用ALTER OUTLINE语句:
ALTER OUTLINE outline_from_text DISABLE;
ALTER OUTLINE outline_from_text ENABLE;
第二个条件是类别
(category)必须在会话或系统级别通过初始化参数use_stored_outlines来激活。初始化参数可以接受的值为TRUE、FALSE或类别名。如果指定TRUE, 类别默认值为DEFAULT。 以下 SQL语句在会话级别激活属于test类别的存储概要:
ALTER SESSION SET use_stored_outlines =
test;
由于初始化参数use_stored_outlines只支持单个类别,因此在同一时间一个会话只能激活一个类别。
要想知道查询优化器是否使用了存储概要,可以利用dbms_xplan包下的函数。实际上,正如下面的例子所示,输出的Note部分明确提供了需要的信息:
EXPLAIN PLAN FOR select * FROM t WHERE n =
1970;
select * from table(dbms_xplan.display);
Note
----
- outline
"OUTLINE_FROM_TEXT" used for
this statement
对于库缓存中存储的游标,v$sql视图的outline_category列会指明在执行计划生成期间是否使用了存储概要。不幸的是,这只给出了类别名。存储概要名本身却是未知的。如果没有使用存储概要,该列将会是NULL。
有一种方法可以知道在一段时间内是否使用过存储概要,可以使用dbms_outln包下的clear_used过程来重置使用标记。接着,稍后再查看该标记,就可以判断是否使用了这个存储概要。然而,并不会给出更多的使用信息(比如,使用次数或何时使用):
execute dbms_outln.clear_used(name
=>'OUTLINE_FROM_TEXT')
select used FROM user_outlines WHERE
name='OUTLINE FROM TEXT';
select * FROM t WHERE n = 1970;
select used FROM user_outlines WHERE
name='OUTLINE FROM TEXT';
1.1.1.3 移动存储概要
Oracle并没有提供用于移动存储概要的特别功能。基本上,必须自己从一个数据字典复制到另一个数据字典。这比较简单,因为数据只保存在了outln模式的三个表中:ol$、ol$hints和ol$nodes。可以使用下面的命令来导入和导出所有可用的存储概要:
exp tables=(outln.ol$,outln.ol$hints,outln.ol$nodes)
file=outln.dmp
imp full=y ignore=y file=outln.dmp
要想移动单个的存储概要(此例中为outline_from_text), 可以给export命令添加以下参数:要想移动一个类别(这里使用test类别)下的所有存储概要,可以给export命令添加以下参数:
query="WHERE category='TEST'"
请小心,因为根据使用的操作系统和shell,你可能必须添加某些转义字符才能成功传递所有参数。例如,在Linux服务器上,使用bash时,我必须执行以下命令:
exp
tables=(outln.ol$,outln.ol$hints,outln.ol$nodes) file=outln.dmp
query="WHERE
ol_name='OUTLINE_FROM_TEXT'"
1.1.1.4 编辑存储概要
使用存储概要可以锁定执行计划。然而,只有在查询优化器能够生成高效执行计划并且稍后捕捉到并由存储概要锁定才有用。如果不是这种情况,首先你需要研究的是,为了创建保存高效执行计划的存储概要,是否有可能修改执行环境、访问结构或对象的统计信息。比如,给定SQL语句的执行计划使用了索引扫描,而你想避免使用它,那就应该在测试系统上删除(或隐藏)索引,生成存储概要,然后移动到生产环境中。
当你发现无法强制查询优化器自动生成一个高效的执行计划时,最后的手段是手工修改存储概要。简单地说,你需要修改与存储概要相关联的hint。然而在实践中,你无法对保存在数据字典中的公共存储概要(public stored outline)(这是到目前为止我们讨论的存储概要种类)简单地运行几个SQL语句。
相反,你必须执行像图11-3总结的那样的修改。这个过程是基于私有存储概要(private stored outline)的修改。这些与公共存储概要类似,但不是保存在数据字典中,而是保存在工作表(working table)中。使用工作表的目的就是为了避免直接修改数据字典。因此,要修改存储概要,你需要创建、修改并测试私有存储概要。接着,当私有存储概要工作正常后,就把它改成公共存储概要。Dbms_outln_edit包和CREATE OUTLINE语句的一些扩展都可以修改存储概要。
图11-3 修改存储概要期间执行的步骤
根据outline_editing.sql脚本中的例子,我来介绍图11-3总结的整个过程。目的是为以下查询创建和修改存储概要,来用索引扫描替代全表扫描:
EXPLAIN PLAN FOR select * FROM t WHERE n =
1970;
select * FROM
table(dbms_xplan.display(NULL,NULL,'basic'));
首先,需要创建私有存储概要。因此,会遇到两种情况。第一种情况是像以下SQL语句那样重新创建私有存储概要。PRIVATE关键字指定了要创建的存储概要类型:
CREATE OR REPLACE PRIVATE OUTLINE
p_outline_editing ON select * FROM t WHERE n = 1970;
第二种情况是借助于类似以下SQL语句复制已经存在于数据字典中的公共存储概要。PRIVATE和PUBLIC关键字分别指定了需要创建和复制的存储概要类型:
CREATE PRIVATE OUTLINE p_outline_editing
FROM PUBLIC outline_editing;
两种方法都会在工作表里创建私有存储概要。下面是与存储概要相关的hint列表:
select hint_text FROM ol$hints WHERE
ol_name='P_OUTLINE_EDITING';
一旦创建好私有存储概要,就可以使用常规DML语句修改它。然而,想要修改覆盖所有需求并不是容易的事。一个比较容易实现的办法是再创建一个私有存储概要来复制想要的执行计划,然后交换这两个执行计划的内容。要创建附加存储概要,需要执行以下SQL语句。请注意,hint是用来命令查询使用全表扫描的:
CREATE OR REPLACE PRIVATE OUTLINE
p_outline_editing_hinted ON select /*+ full(t) */ * FROM t WHERE n = 1970;
然后通过执行如下SQL语句来交换内容:
UPDATE ol$
SET hintcount =(select hintcount FROM ol$ WHERE ol_name
='P_OUTLINE_EDITING_HINTED')
WHERE ol_name='P_OUTLINE_EDITING';
DELETE ol$hints WHERE ol_name
='P_OUTLINE_EDITING';
UPDATE olShints SET ol_name
='P_OUTLINE_EDITING' WHERE ol_name ='P_OUTLINE_EDITING_HINTED';
下面是交换完后与私有存储概要相关联的hint列表。唯一的不同就是index
hint被替换成了full
hint:
select
hint_text FROM ol$hints WHERE ol_name ='P_OUTLINE_EDITING';
为了确保内存中的存储概要同步修改,可以执行以下PL/SQL调用:
execute
dbms_outln_edit.refresh_private_outline('P_OUTLINE_EDITING');
接着,将初始化参数use_private_outlines设置为TRUE或指定私有存储概要所属的类别名来激活和测试私有存储概要。请注意执行计划里的全表扫描和Note部分里的信息,它们都确认了使用私有存储概要。例如:
ALTER SESSION SET use_private_outlines =
TRUE;
EXPLAIN PLAN FOR select * FROM t WHERE n
=1970;
select * FROM
table(dbms_xplan.display(NULL,NULL,'basic +note'));
Note
----
- outline "P_OUTLINE_EDITING"
used for this statement
一旦你满意现有的私有存储概要,就可以使用以下SQL语句将它当作公共存储概要进行发布:
CREATE PUBLIC OUTLINE outline_editing FROM
PRIVATE p_outline_editing;
1.1.1.1 删除存储概要
使用DROP OUTLINE语句或dbms_outln包下的drop_by_cat过程,可以删除存储概要。前者删除单个存储概要,而后者删除一个类别下的所有存储概要:
DROP OUTLINE outline_from_text;
execute dbms_outln.drop_by_cat(cat
=>'TEST');
要删除私有存储概要,必须使用DROP
PRIVATE OUTLINE语句。
1.1.1.2 权限
创建、修改和删除存储概要需要的系统权限分别是create any outline、alter any outline和drop any outline。对存储概要来说不存在对象权限。
默认情况下,只有拥有dba或execute_catalog_role角色的用户才能执行dbms_outln包。相反,所有用户都可以执行dbms_outln_edit包下的程序(已将execute权限赋予public)。
终端用户不需要特定权限也可以使用存储概要。
提示 你永远不需要使用outln账户登录。因此,出于安全考虑,应该锁定该帐户或修改默认密码。这很重要,因为该帐户拥有一个非常危险的系统权限:execute any procedure。