oracle 9i,10g,11g执行计划的固定一直是oracle的心病之一,虽然CBO很强了,在10g里我们用outline,引入sql_profile,11g里用sql_profile,引入sql_plan_baseline
1 相关参数
optimizer_capture_sql_plan_baselines默认值false,即不采用自动捕获
optimizer_use_sql_plan_baselines 默认值true,即优先使用dba_sql_plan_baselines里的执行计划;
2 默认参数测试
有条SQL很简单:select * from hr.emp where EMPLOYEE_ID=206;
此表无索引,无主键,就是一个最普通的堆表
执行计划无悬念:全表扫苗:
SQL> select* from table(dbms_xplan.display_cursor('76rrg3a06j24n',0));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID 76rrg3a06j24n, child number 0
-------------------------------------
select * from hr.emp where EMPLOYEE_ID=206
Plan hash value: 3956160932
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 2 (100)| |
|* 1 | TABLE ACCESS FULL| EMP | 1 | 133 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------
建立基线:
declare
v_ret varchar2(100);
begin
v_ret := dbms_spm.load_plans_from_cursor_cache(sql_id=>'76rrg3a06j24n');
end;
检查基线:
select sql_handle,sql_text,origin,version,enabled,accepted,fixed from dba_sql_plan_baselines
SQL_HANDLE SQL_TEXT ORIGIN VERSION ENABLED ACCEPTED FIXED
SQL_4313a2ffff308f08 MANUAL-LOAD 11.2.0.4.0 YES YES NO
建立索引:create index i_employee_id on hr.emp(employee_Id);
再次执行,检查child_number:
select sql_id,child_number,sql_text ,child_Number from v$sql where sql_text like 'select %hr.emp%';
SQL_ID CHILD_NUMBER SQL_TEXT CHILD_NUMBER
76rrg3a06j24n 1 select * from hr.emp where EMPLOYEE_ID=206 1
查看其执行计划:
Plan hash value: 3956160932
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 266 | 2 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| EMP | 2 | 266 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("EMPLOYEE_ID"=206)
Note
-----
- SQL plan baseline "SQL_PLAN_464x2zzzm13s8d8a279cc" used for this statement
依旧全表扫描,这是不科学的,理论上是走索引,他给出了说明,走的是SQL_PLAN_464x2zzzm13s8d8a279cc,检查dba_sql_plan_baselines
SQL_HANDLE SQL_TEXT ORIGIN VERSION ENABLED ACCEPTED FIXED PLAN_NAME
SQL_4313a2ffff308f08 MANUAL-LOAD 11.2.0.4.0 YES YES NO SQL_PLAN_464x2zzzm13s8d8a279cc
SQL_4313a2ffff308f08 AUTO-CAPTURE 11.2.0.4.0 YES NO NO SQL_PLAN_464x2zzzm13s8f5e7e8ee
这里其实他是捕捉到另一个执行计划了,但是,由于没启用,就是全表扫描了,启用新的执行计划:
declare
tp clob;
begin
tp:=dbms_spm.evolve_sql_plan_baseline(sql_handle=>'SQL_4313a2ffff308f08',plan_name=>'SQL_PLAN_464x2zzzm13s8f5e7e8ee');
end;
再次运行:
Plan hash value: 1162342648
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 133 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 133 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | I_EMPLOYEE_ID | 1 | | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("EMPLOYEE_ID"=206)
Note
-----
- dynamic sampling used for this statement (level=2)
- SQL plan baseline "SQL_PLAN_464x2zzzm13s8f5e7e8ee" used for this statement
好了,走正常的执行计划了,再次删除较优的base_line:
declare v_ret varchar2(100);
begin
v_ret:= dbms_spm.drop_sql_plan_baseline(sql_handle=>'SQL_4313a2ffff308f08',plan_name=>'SQL_PLAN_464x2zzzm13s8f5e7e8ee');
end ;
毫无疑问,又走全表扫描了,但dba_hist_sql_plan_baselines里依旧是生成了的,只是没有启用而已;
修改状态:
declare
v_ret varchar2(100);
begin
v_ret := dbms_spm.alter_sql_plan_baseline(
sql_handle=>'SQL_4313a2ffff308f08',
plan_name=>'SQL_PLAN_464x2zzzm13s8d8a279cc',
attribute_name=>'enabled',
attribute_value=>'NO'
);
end;
查看执行计划,两个都没启用了,这和修改参数(optimizer_use_sql_plan_baselines=false)是一样的:
Execution Plan
----------------------------------------------------------
Plan hash value: 1162342648
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 133 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 133 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | I_EMPLOYEE_ID | 1 | | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("EMPLOYEE_ID"=206)
Note
-----
- dynamic sampling used for this statement (level=2)