I’m just wondering… be careful it is part of Oracle Tuning Pack 11g !
Anyway, before you answer this question, lets illustrate with an example how the “non-automatic”, “no tuning Pack” SQL Plan Management works. This 11g new feature completes (replaces ?) the outlines by providing a new plan stability capability to Oracle database 11g Enterprise Edition.
If you want the truth behind this example, you should refer before anything else to the 11g documentation :
- Oracle 11g Reference :
- optimizer_capture_sql_plan_baselines (Tuning Pack)
Step 1 : Sample schema
We’ll need a table with a couple of rows to demonstrate this feature :
create table gark (id number not null); begin for i in 1..10000 loop insert into gark(id) values (i); end loop; commit; end; / exec dbms_stats.gather_table_stats(user, 'GARK')
Step 2 : Query and Plan
We’ll need to use SQL*Plus ! I haven’t been able to use SQL*Developer as the last session query, from my experience is not the one you’ve just run even with “set serveroutput off” and I don’t want to use “explain plan” neither “set autotrace on”. Connected as the sample user (Let’s say SCOTT), run :
set serveroutput off select a.id, b.id from gark a, gark b where a.id=500 and b.id=a.id; select * from table(dbms_xplan.display_cursor);
The query plan should look like this :
Execution Plan ---------------------------------------------------------- Plan hash value: 2625395012 ---------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| ---------------------------------------------------------------- | 0 | SELECT STATEMENT | | 4 | 24 | 23 (5)| |* 1 | HASH JOIN | | 4 | 24 | 23 (5)| |* 2 | TABLE ACCESS FULL| GARK | 2 | 6 | 11 (0)| |* 3 | TABLE ACCESS FULL| GARK | 2 | 6 | 11 (0)| ---------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("B"."ID"="A"."ID") 2 - filter("A"."ID"=500) 3 - filter("B"."ID"=500)
Step 3 : Register the plan in the SQL Plan Baselines
To perform this operation, you have to know the query SQL_ID in addition to the PLAN_HASH_VALUE that is display in the plan output :
accept plan_hash_value prompt "Enter value for plan_hash_value: " 2625395012 select distinct sql_id, plan_hash_value from v$sql_plan where plan_hash_value=&plan_hash_value; SQL_ID PLAN_HASH_VALUE ------------- --------------- 4pznd20f4x8tf 2625395012
The query below display the registered SQL Plan baselines. It should be empty unless we’ve set the optimizer_capture_sql_plan_baselines parameter to true :
col SQL_HANDLE format a24 col PLAN_NAME format a29 col SCHEMA format a8 select SQL_HANDLE, PLAN_NAME, PARSING_SCHEMA_NAME SCHEMA, ENABLED, ACCEPTED, SQL_TEXT from dba_sql_plan_baselines; no row selected.
You can now register the current query plan with the script below :
accept sql_id prompt "Enter value for sql_id: " 4pznd20f4x8tf declare gg binary_integer; begin gg:=dbms_spm.load_plans_from_cursor_cache( sql_id=>'&sql_id'); end; / PL/SQL procedure successfully completed. select SQL_HANDLE, PLAN_NAME, PARSING_SCHEMA_NAME SCHEMA, ENABLED, ACCEPTED, SQL_TEXT from dba_sql_plan_baselines; SQL_HANDLE PLAN_NAME SCHEM ENA ACC ------------------------ ----------------------------- ----- --- --- SQL_TEXT -------------------------------------------------------------------- SYS_SQL_9ff3489361e958aa SYS_SQL_PLAN_61e958aa45bb399b SCOTT YES YES select a.id, b.id from gark a, gark b where a.id=500 and b.id=a.id
Step 4 : the registered SQL Plan Baseline prevents an other plan to be used
To check the behavior of of the SQL Plan Management feature, we’ll perform what follow :
- Create an index the query could benefit from
- Check the content of the SQL Plan Baselines after the index creation
- Flush the Shared Pool and check the plan will be parsed
- Run the query and check its plan
1- Create an index
create index gark_idx on gark(id); Index created.
2- Look at the SQL Plan baselines with the DBMS_XPLAN.DISPLAY_SQL_PLAN_BASELINE table function ; The registered plan does not use the index :
accept sql_handle prompt "Enter value for sql_handle: " SYS_SQL_9ff3489361e958aa select * from table( dbms_xplan.display_sql_plan_baseline( sql_handle=>'&sql_handle')); PLAN_TABLE_OUTPUT ---------------------------------------------------------------- SQL handle: SYS_SQL_9ff3489361e958aa SQL text: select a.id, b.id from gark a, gark b where a.id=500 and b.id=a.id ---------------------------------------------------------------- ---------------------------------------------------------------- Plan name: SYS_SQL_PLAN_61e958aa45bb399b Enabled: YES Fixed: NO Accepted: YES Origin: MANUAL-LOAD ---------------------------------------------------------------- Plan hash value: 2625395012 ---------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| ---------------------------------------------------------------- | 0 | SELECT STATEMENT | | 4 | 24 | 23 (5)| |* 1 | HASH JOIN | | 4 | 24 | 23 (5)| |* 2 | TABLE ACCESS FULL| GARK | 2 | 6 | 11 (0)| |* 3 | TABLE ACCESS FULL| GARK | 2 | 6 | 11 (0)| ---------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("B"."ID"="A"."ID") 2 - filter("A"."ID"=500) 3 - filter("B"."ID"=500) ERROR: User has no SELECT privileges on objects of the SQL plan baseline
3- Flush the shared Pool :
alter system flush shared_pool; System altered. accept sql_id prompt "Enter value for sql_id: " 4pznd20f4x8tf select sql_id, plan_hash_value from v$sql where sql_id='&sql_id'; no rows selected
4- Run the query and figured out that the query plan does not change besides the new index and the fact it has been re-parsed :
set serveroutput off select a.id, b.id from gark a, gark b where a.id=500 and b.id=a.id; select * from table(dbms_xplan.display_cursor); Execution Plan ---------------------------------------------------------- Plan hash value: 2625395012 ---------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| ---------------------------------------------------------------- | 0 | SELECT STATEMENT | | 4 | 24 | 23 (5)| |* 1 | HASH JOIN | | 4 | 24 | 23 (5)| |* 2 | TABLE ACCESS FULL| GARK | 2 | 6 | 11 (0)| |* 3 | TABLE ACCESS FULL| GARK | 2 | 6 | 11 (0)| ---------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("B"."ID"="A"."ID") 2 - filter("A"."ID"=500) 3 - filter("B"."ID"=500) Note ----- - SQL plan baseline SYS_SQL_PLAN_61e958aa45bb399b used for this statement
If you display the SQL plan baselines, you’ll see a new plan for the query has been registered which attributes are ACCEPTED=’NO’ and ORIGIN=’AUTO-CAPTURE’. This plan is not used by the query :
select SQL_HANDLE, PLAN_NAME, PARSING_SCHEMA_NAME SCHEMA, ENABLED, ACCEPTED, SQL_TEXT from dba_sql_plan_baselines; SQL_HANDLE PLAN_NAME SCHEM ENA ACC ------------------------ ----------------------------- ----- --- --- SQL_TEXT -------------------------------------------------------------------- SYS_SQL_9ff3489361e958aa SYS_SQL_PLAN_61e958aa45bb399b SCOTT YES YES select a.id, b.id from gark a, gark b where a.id=500 and b.id=a.id SYS_SQL_9ff3489361e958aa SYS_SQL_PLAN_61e958aafcd784aa SCOTT YES NO select a.id, b.id from gark a, gark b where a.id=500 and b.id=a.id accept sql_handle prompt "Enter value for sql_handle: " SYS_SQL_9ff3489361e958aa select * from table( dbms_xplan.display_sql_plan_baseline( sql_handle=>'&sql_handle')); PLAN_TABLE_OUTPUT ---------------------------------------------------------------- SQL handle: SYS_SQL_9ff3489361e958aa SQL text: select a.id, b.id from gark a, gark b where a.id=500 and b.id=a.id ---------------------------------------------------------------- ---------------------------------------------------------------- Plan name: SYS_SQL_PLAN_61e958aa45bb399b Enabled: YES Fixed: NO Accepted: YES Origin: MANUAL-LOAD ---------------------------------------------------------------- Plan hash value: 2625395012 ---------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| ---------------------------------------------------------------- | 0 | SELECT STATEMENT | | 4 | 24 | 23 (5)| |* 1 | HASH JOIN | | 4 | 24 | 23 (5)| |* 2 | TABLE ACCESS FULL| GARK | 2 | 6 | 11 (0)| |* 3 | TABLE ACCESS FULL| GARK | 2 | 6 | 11 (0)| ---------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("B"."ID"="A"."ID") 2 - filter("A"."ID"=500) 3 - filter("B"."ID"=500) ---------------------------------------------------------------- Plan name: SYS_SQL_PLAN_61e958aafcd784aa Enabled: YES Fixed: NO Accepted: NO Origin: AUTO-CAPTURE ---------------------------------------------------------------- Plan hash value: 886284999 ------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%)| ------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 6 | 3 (34)| | 1 | MERGE JOIN CARTESIAN| | 1 | 6 | 3 (34)| |* 2 | INDEX RANGE SCAN | GARK_IDX | 1 | 3 | 1 (0)| | 3 | BUFFER SORT | | 1 | 3 | 2 (50)| |* 4 | INDEX RANGE SCAN | GARK_IDX | 1 | 3 | 1 (0)| ------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("A"."ID"=500) 4 - access("B"."ID"=500)
Step 5 : Evolve the plan
The evolve_sql_plan_baseline enable to accept the new SQL plan :
accept sql_handle prompt "Enter value for sql_handle: " SYS_SQL_9ff3489361e958aa var v_out clob exec :v_out:=dbms_spm.evolve_sql_plan_baseline(- sql_handle=>'&sql_handle') print v_out select SQL_HANDLE, PLAN_NAME, ENABLED, ACCEPTED from dba_sql_plan_baselines; SQL_HANDLE PLAN_NAME ENA ACC ------------------------ ----------------------------- --- --- SYS_SQL_9ff3489361e958aa SYS_SQL_PLAN_61e958aa45bb399b YES YES SYS_SQL_9ff3489361e958aa SYS_SQL_PLAN_61e958aafcd784aa YES YES
Once accepted, if you execute the query, the new plan (with the index) will be used :
select a.id, b.id from gark a, gark b where a.id=500 and b.id=a.id; select * from table(dbms_xplan.display_cursor); PLAN_TABLE_OUTPUT ---------------------- SQL_ID 4pznd20f4x8tf, child number 0 ------------------------------------- select a.id, b.id from gark a, gark b where a.id=500 and b.id=a.id Plan hash value: 886284999 ------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%)| ------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 6 | 3 (34)| | 1 | MERGE JOIN CARTESIAN| | 1 | 6 | 3 (34)| |* 2 | INDEX RANGE SCAN | GARK_IDX | 1 | 3 | 1 (0)| | 3 | BUFFER SORT | | 1 | 3 | 2 (50)| |* 4 | INDEX RANGE SCAN | GARK_IDX | 1 | 3 | 1 (0)| ------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("A"."ID"=500) 4 - access("B"."ID"=500) Note ----- - SQL plan baseline SYS_SQL_PLAN_61e958aafcd784aa used for this statement
Step 6 : One Step Back
What if I just accept a new plan and it is indeed not efficient ? You can of cause drop the plan from the SQL Plan baselines as described in Step 7 or you can use the FIXED attribute which set the plans that have to be used. Using this attribute enables you to easily go back and forward between a set of plans :
accept sql_handle prompt "Enter value for sql_handle: " SYS_SQL_9ff3489361e958aa accept plan_name prompt "Enter value for plan_name: " SYS_SQL_PLAN_61e958aa45bb399b declare v_out binary_integer; begin v_out := dbms_spm.ALTER_SQL_PLAN_BASELINE( sql_handle =>'&sql_handle', plan_name=>'&plan_name', attribute_name=>'fixed', attribute_value=>'YES'); end; / select SQL_HANDLE, PLAN_NAME, FIXED, ACCEPTED from dba_sql_plan_baselines; SQL_HANDLE PLAN_NAME FIX ACC ------------------------ ----------------------------- --- --- SYS_SQL_9ff3489361e958aa SYS_SQL_PLAN_61e958aa45bb399b YES YES SYS_SQL_9ff3489361e958aa SYS_SQL_PLAN_61e958aafcd784aa NO YES select a.id, b.id from gark a, gark b where a.id=500 and b.id=a.id; select * from table(dbms_xplan.display_cursor); Execution Plan ---------------------------------------------------------- Plan hash value: 2625395012 ---------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| ---------------------------------------------------------------- | 0 | SELECT STATEMENT | | 4 | 24 | 23 (5)| |* 1 | HASH JOIN | | 4 | 24 | 23 (5)| |* 2 | TABLE ACCESS FULL| GARK | 2 | 6 | 11 (0)| |* 3 | TABLE ACCESS FULL| GARK | 2 | 6 | 11 (0)| ---------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("B"."ID"="A"."ID") 2 - filter("A"."ID"=500) 3 - filter("B"."ID"=500) Note ----- - SQL plan baseline SYS_SQL_PLAN_61e958aa45bb399b used for this statement
To re set the fixed attribute run what’s below :
accept sql_handle prompt "Enter value for sql_handle: " SYS_SQL_9ff3489361e958aa accept plan_name prompt "Enter value for plan_name: " SYS_SQL_PLAN_61e958aa45bb399b declare v_out binary_integer; begin v_out := dbms_spm.ALTER_SQL_PLAN_BASELINE( sql_handle =>'&sql_handle', plan_name=>'&plan_name', attribute_name=>'fixed', attribute_value=>'NO'); end; / select SQL_HANDLE, PLAN_NAME, FIXED, ACCEPTED from dba_sql_plan_baselines; SQL_HANDLE PLAN_NAME FIX ACC ------------------------ ----------------------------- --- --- SYS_SQL_9ff3489361e958aa SYS_SQL_PLAN_61e958aa45bb399b NO YES SYS_SQL_9ff3489361e958aa SYS_SQL_PLAN_61e958aafcd784aa NO YES
Step 7. Drop the SQL Plan baselines
To drop the registered SQL Plan baselines, you can use the drop_sql_plan_baseline function from dbms_spm as below :
col schema format a5 col SQL_HANDLE format a24 select SQL_HANDLE, PLAN_NAME, PARSING_SCHEMA_NAME SCHEMA, ENABLED, ACCEPTED from dba_sql_plan_baselines; SQL_HANDLE PLAN_NAME SCHEM ENA ACC ------------------------ ----------------------------- ----- --- --- SYS_SQL_9ff3489361e958aa SYS_SQL_PLAN_61e958aa45bb399b SCOTT YES YES SYS_SQL_9ff3489361e958aa SYS_SQL_PLAN_61e958aafcd784aa SCOTT YES YES accept sql_handle prompt "Enter value for sql_handle: " SYS_SQL_9ff3489361e958aa accept plan_name prompt "Enter value for plan_name: " SYS_SQL_PLAN_61e958aa45bb399b DECLARE gg binary_integer; BEGIN gg:=DBMS_SPM.DROP_SQL_PLAN_BASELINE( SQL_HANDLE => '&sql_handle', PLAN_NAME => '&plan_name'); END; / accept plan_name prompt "Enter value for plan_name: " SYS_SQL_PLAN_61e958aafcd784aa DECLARE gg binary_integer; BEGIN gg:=DBMS_SPM.DROP_SQL_PLAN_BASELINE( SQL_HANDLE => '&sql_handle', PLAN_NAME => '&plan_name'); END; / col schema format a5 col SQL_HANDLE format a24 select SQL_HANDLE, PLAN_NAME, PARSING_SCHEMA_NAME SCHEMA, ENABLED, ACCEPTED from dba_sql_plan_baselines; no rows selected
Step 8. Introducing Automatic SQL Plan Management
In order to use Automatic SQL Plan Management, you’ll have to licence Oracle Tuning Pack and set optimizer_capture_sql_plan_baselines to true. Once done :
- The instances capture the “repeatable” SQL and their plan during the “normal” period.
- The new automatic maintenance schedule runs the SQL Tuning Advisor and set the SQL plans. As with 10g the SQL Tuning Advisor does not only consider the statistics but goes much deeper into SQL and plan analysis to evolve the plan…
- While new plans can be consider, the SQL Tuning Advisor evolves plans during the Maintenance Window making plans probably more accurate and probably less candidate to sudden changes.
It looks a fantastic 11g feature, don’t you think ? The question now is what performance enhancement I should expect from it and a good way to evaluate it would be to run the command below on a production system… Who dares ?
alter system set optimizer_capture_sql_plan_baselines=true
Step 9 : Drop the sample schema
drop index gark_idx; drop table gark;
Step 10 : To conclude
No doubt that in manual mode SQL Plan Management highly improves the way we manage plan stability. It enables to easily switch from one plan to the other, it can be exported/imported from one database to the other and as a good news **considering the best 11g new features seem to be part of options**, it’s part of the classic “EE”. Now I miss a real production use case of Automatic SQL Plan Management.
5 Comments. Leave new
where is it documented that optimizer_capture_sql_plan_baselines is part of the Tuning Pack?
followed the link at top “optimizer_capture_sql_plan_baselines (Tuning Pack)” but didn’t see any reference to the Tuning Pack.
Did a google search but didn’t see anything in the first 10 hits
here’s a link:
https://68.142.116.68/docs/cd/B28359_01/license.111/b28287/options.htm
says
“Automatic Plan Evolution of SQL Plan Management”
is part of the Tuning pack. Still unclear as to which commands are part of the pack and which or not.
Hello,
It looks like, I was wrong when I wrote that… Even if Oracle is not very clear about the SPM feature that is actually included in the Tuning Pack.
If you refer to the licensing guide, regarding Tuning Pack), it says:
Oracle Tuning Pack includes the following features:
[…]
* Automatic Plan Evolution of SQL Plan Management
The question is What is “Automatic Plan Evolution of SQL Plan Management” ? Obviously if you search the documentation for this feature, you find it only in the Licensing Guide.
I’ve disabled the Tuning pack and change the value and it doesn’t fail. The licensing terms suggest that’s actually included in that case! or, maybe, Oracle disables the feature, in spite of the parameter and doesn’t tell:
SQL> alter system set control_management_pack_access=DIAGNOSTIC;
System altered.
SQL> alter system set OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES=true;
System altered.
So it’s worth more testing (at least to me) to figure out what exact part of SPM is included in the Tuning Pack Only!
SQL Plan Management is not part of the Tuning Pack. It is a free tool available to all users of Oracle Enterprise Edition. What Kyle found in the Oracle documentation is a documentation bug. The wording implies you need the Tuning Pack when you don’t. SQL Plan Management works in conjunction with the Tuning Pack specifically the auto SQL Tune feature but is not part of it.
Automatic Plan Evolution of SQL Plan Management means if the Auto SQL Tune advisor finds a better plan for a SQL statement that has a SQL Plan Baseline that plan will be automatically added to the plan baseline as an accepted plan. Nothing more. More info on this can be found in this white paper https://www.oracle.com/technetwork/database/features/bi-datawarehousing/twp-sql-plan-management-11gr2-133099.pdf
Hi, here is an updated link. Maria’s didn’t work… Here is one that works now:
https://www.oracle.com/technetwork/database/focus-areas/bi-datawarehousing/twp-sql-plan-management-11gr2-133099.pdf