An execution plan describes how Oracle will retrieve the data to satisfy the results of a query. When you submit a SQL statement, the query optimizer quickly produces several execution plans and will determine which plan is most efficient. In most scenarios, the prior behavior results in a well-performing execution plan. However, you will encounter situations where you know additional details about your environment and need to adjust the optimizer's choice of an execution plan. Listed next are features you can use to influence the decision path the optimizer uses when selecting a plan:
It's critical you understand how these features affect the optimizer's choice of an execution plan. When troubleshooting SQL performance problems, you must determine which of the prior features are enabled and how they influence query behavior. The performance of a SQL statement can vary drastically depending on which feature is implemented and the impact of the various combinations of features.
Initialization parameters (that impact the optimizer) and statistics gathering are detailed in Chapter 13. Using hints is the emphasis of Chapter 14. The focus of this chapter is SQL profiles and plan baselines.
SQL profiles are optionally generated corrections and improvements to statistics. The recommendation (and code) to implement a SQL profile is manifested through the output of the SQL Tuning Advisor. You can manually enable SQL profiles or configure them to be automatically accepted. SQL profiles help the optimizer derive better execution plans.
SQL plan management allows you to store and manage execution plans within tables in the database. Plan baselines consist of one or more stored execution plans that have been accepted for a SQL query. When you run a query, and if a plan baseline exists for the query, the optimizer will give precedence to execution plans within the plan baseline. Plan history is the super set of both accepted and unaccepted execution plans for a query. You can manually change the state of an unaccepted plan to accepted (this moves it to the plan baseline). This is known as evolving a plan baseline.
Plan baselines help ensure that the optimizer consistently chooses the same execution plan, regardless of changes in the database environment. Plan baselines provide the following benefits:
Figure 12-1 displays the flow of choices that the optimizer makes when choosing an execution plan. Please take a few minutes to analyze this diagram and ensure you grasp how the various features influence the optimizer's behavior. As you view the diagram, keep in mind the following:
As you look at the skep-shaped diagram, to help understand how the optimizer chooses between the low-cost plan and a plan baseline plan, consider the general steps taken when formulating an execution plan:
Figure 12-1. Oracle database features influencing optimizer's choice of execution plan
Features such as initialization parameters and hints don't require an extra license and are available with all editions of the Oracle database. Other features such as SQL profiles require an extra license and ship only with the Enterprise Edition. Table 12-1 summarizes the characteristics of each query optimizer-influencing feature.
The first part of this chapter focuses on managing SQL profiles. The rest of the chapter deals with the implementation and use of plan baselines. We describe practical and real-world examples of the use of these tools. Where appropriate, we also have added instructions on how to use a given feature via Enterprise Manager.
You have a poorly performing query, and you want to get advice from the SQL Tuning Advisor. You realize that the SQL Tuning Advisor may recommend that a SQL profile be applied to the problem query as part of the tuning recommendation.
Run the SQL Tuning Advisor for the problem query. Keep in mind that the SQL Tuning Advisor may or may not recommend a SQL profile as a solution for performance issues. To run the SQL Tuning Advisor manually, perform the following steps:
DBMS_SQLTUNE
to create a tuning task.The following example follows the prior steps. In this scenario, the SQL Tuning Advisor recommends that a SQL profile be applied to the given query.
Tip See Chapter 11 for complete details on creating SQL tuning tasks. Chapter 11 covers topics such as using the AWR, memory, or SQL tuning sets as the source of SQL for a tuning task.
The first step is to create a tuning task that is associated with the problem SQL statement. In the following code, the SQL text is hard-coded as input to the tune_sql
variable:
DECLARE
tune_sql CLOB;
tune_task VARCHAR2(30);
BEGIN
tune_sql := 'select count(*) from mgmt_db_feature_usage_ecm2';
tune_task := DBMS_SQLTUNE.CREATE_TUNING_TASK(
sql_text => tune_sql
,user_name => 'STAGING'
,scope => 'COMPREHENSIVE'
,time_limit => 60
,task_name => 'TUNE1'
,description => 'Calling SQL Tuning Advisor for one statement'
);
END;
/
The prior code is placed in a file named sqltune.sql
, and executed as follows:
SQL> @sqltune.sql
If you need to later drop the tuning task, you can use the DBMS_SQLTUNE.DROP_TUNING_TASK
procedure. Obviously don't drop the tuning task at this point because you'll need it for the next several steps.
Note When working with tuning advice and SQL profiles, ensure that the database account you're using has the ADMINISTER SQL MANAGEMENT OBJECT
system privilege granted to it. This privilege contains all of the privileges required to manage tuning tasks and SQL profiles.
This step runs the SQL Tuning Advisor to generate advice regarding any queries associated with the tuning task (created in step 1):
SQL> exec dbms_sqltune.execute_tuning_task(task_name=>'TUNE1'),
Now use DBMS_SQLTUNE
to extract any tuning advice generated in step 2:
set long 10000
set longchunksize 10000
set lines 132
set pages 200
select dbms_sqltune.report_tuning_task('TUNE1') from dual;
For this example, the SQL Tuning Advisor recommends creating a SQL profile. Here is a snippet from the output that contains the recommendation and the code required to create the SQL profile:
Recommendation (estimated benefit: 86.11%)
------------------------------------------
- Consider accepting the recommended SQL profile to use parallel execution
for this statement.
execute dbms_sqltune.accept_sql_profile(task_name => 'TUNE1', task_owner
=> 'SYS', replace => TRUE, profile_type =>
DBMS_SQLTUNE.PX_PROFILE);
-------------------------------------------
Executing this query parallel with DOP 8 will improve its response time
86.11% over the original plan. However, there is some cost in enabling
parallel execution...
To actually create the SQL profile, you need to run the code recommended by the SQL Tuning Advisor (from step 3)—for example:
begin
-- This is the code from the SQL Tuning Advisor
dbms_sqltune.accept_sql_profile(
task_name => 'TUNE1',
task_owner => 'SYS',
replace => TRUE,
profile_type => DBMS_SQLTUNE.PX_PROFILE);
--
end;
/
When the prior code is run, it creates and enables the SQL profile. Now whenever the associated SQL query is executed, the SQL profile will be considered by the optimizer when formulating an execution plan.
Tip How do you know if a SQL profile is being used by the optimizer? Set AUTOTRACE
on and view the execution plan with the profile enabled and then disabled. You should see a lower-cost execution plan being used when the profile is enabled. Additionally, consider inspecting the SQL_PROFILE
column of V$SQL
.
The only Oracle-supported method for creating a SQL profile is to run the SQL Tuning Advisor and if recommended, create a SQL profile using the Tuning Advisor's output. In other words, the SQL Tuning Advisor determines if a SQL profile will help, and if so generates the code required to create a SQL profile for a given query.
The “Solution” section detailed how to manually run the SQL Tuning Advisor. Keep in mind that as of Oracle Database 11g, this tuning task job automatically runs on a regularly scheduled basis. See Chapter 11 for details on automatic SQL tuning features. You can easily review the output of the automatic tuning job via this query:
SQL> SELECT DBMS_AUTO_SQLTUNE.REPORT_AUTO_TUNING_TASK FROM DUAL;
We recommend that you review the output of the automatic tuning job on a regular basis. The SQL Tuning Advisor will provide the code to create and accept SQL profiles as part of the output.
As noted in the “Solution” section, a SQL profile is created and accepted via the DBMS_SQLTUNE.ACCEPT_SQL_PROFILE
procedure. There are many options available when using this procedure (see Table 12-2 for details).
The FORCE_MATCH
parameter of ACCEPT_SQL_PROFILE
requires further explanation. Recall that a SQL profile is associated with a SQL statement. The SQL statement is identified via a hash function (SQL signature). The hash function is generated after converting the SQL text and removing extra white space. When setting FORCE_MATCH
to TRUE
, this additionally normalizes literal values into bind values. This is similar to the algorithm generated via the FORCE
option of the CURSOR_SHARING
database initialization parameter.
For example, with FORCE_MATCH
set to TRUE
, the following two SQL statements will generate the same SQL signature:
SQL> select value from my_table where value = 'AA';
SQL> select value from my_table where value = 'bb';
This allows SQL statements that use literal values to share the same SQL profile. If there is a combination of literal values and bind variables in a SQL statement, then literal values are not normalized.
SQL PROFILE VS. DATABASE PROFILE
You realize that the Automatic SQL Tuning job runs on a daily basis (in Oracle Database 11g or higher). You determine that the automatic tuning job generates reasonable SQL profiles for problematic queries and now want to enable the automatic acceptance of SQL profiles generated by the automatic tuning job.
Tip See Chapter 11 for full details on modifying the Automatic SQL Tuning job.
Use the DBMS_AUTO_SQLTUNE.SET_AUTO_TUNING_TASK_PARAMETER
procedure to enable the automatic acceptance of SQL profiles recommended by the Automatic SQL Tuning task—for example:
BEGIN
DBMS_AUTO_SQLTUNE.SET_AUTO_TUNING_TASK_PARAMETER(
parameter => 'ACCEPT_SQL_PROFILES', value => 'TRUE'),
END;
/
If you want to disable the automatic acceptance of SQL profiles, then do so as follows (using the FALSE
parameter):
BEGIN
DBMS_AUTO_SQLTUNE.SET_AUTO_TUNING_TASK_PARAMETER(
parameter => 'ACCEPT_SQL_PROFILES', value => 'FALSE'),
END;
/
Note The DBMS_AUTO_SQLTUNE
package requires the DBA role or that EXECUTE
on the package has been granted explicitly to a user. This package is available in Oracle Database 11g R2 or higher. If you are using a lower version of the database, then use the DBMS_SQLTUNE
package.
In Oracle Database 11g or higher, an automatically configured job runs the SQL Tuning Advisor on a periodic basis (determined by a configured maintenance window). This job identifies high resource-consuming SQL statements from performance metrics contained in the AWR. When the automatic tuning job runs, it will occasionally recommend that a SQL profile be implemented for a poorly performing SQL statement. Oracle will automatically accept the profile if the following conditions are true:
DBMS_AUTO_SQLTUNE.SET_AUTO_TUNING_TASK_PARAMETER
.You can report on the details of the automatic tuning task configuration via this query:
SELECT
parameter_name
,parameter_value
FROM dba_advisor_parameters
WHERE task_name = 'SYS_AUTO_SQL_TUNING_TASK'
AND parameter_name
IN ('ACCEPT_SQL_PROFILES',
'MAX_SQL_PROFILES_PER_EXEC',
'MAX_AUTO_SQL_PROFILES',
'EXECUTION_DAYS_TO_EXPIRE'),
Here is some sample output:
PARAMETER_NAME PARAMETER_VALUE
------------------------- --------------------
EXECUTION_DAYS_TO_EXPIRE 30
ACCEPT_SQL_PROFILES TRUE
MAX_SQL_PROFILES_PER_EXEC 20
MAX_AUTO_SQL_PROFILES 10000
Tip SQL profiles that have automatically been implemented display the value of AUTO
in the TYPE
column of the DBA_SQL_PROFILES
view.
You can also use Enterprise Manager to configure the automatic acceptance of SQL profiles. From the main database page, navigate to the Advisor Central page. Next, click the SQL Advisors link. Now click the Automatic SQL Tuning Results page. Next click the configure button of Automatic Implementation of SQL Profiles. You should see a page similar to Figure 12-2.
Figure 12-2. Configuring automatic acceptance of SQL profiles
From this screen, you can manage features such as the automatic acceptance of SQL profiles, maximum time for a tuning session, and so on.
You have created and accepted several SQL profiles and now want to view information related to these database objects.
Use the DBA_SQL_PROFILES
view to display information about SQL profiles. Here's an example that selects the most interesting columns:
SQL> select name, type, status, sql_text from dba_sql_profiles;
Here is a snippet of the output:
NAME TYPE STATUS SQL_TEXT
------------------------------ ------- -------- ------------------------------
SYS_SQLPROF_012eda58a1be0001 MANUAL ENABLED SELECT ecm_snapshot_id AS id...
SYS_SQLPROF_012ea20305980000 MANUAL ENABLED SELECT * FROM inv_maint...
SYS_SQLPROF_012edf0316930003 MANUAL ENABLED SELECT /* + parallel(mgmt_db_f...
For this database, there are several manually enabled SQL profiles (as shown in the prior output).
Note Since a SQL profile is associated with a specific SQL statement (and not a user), there are no ALL
- or USER
-level views associated with SQL profiles.
Recall that a SQL profile contains improvements to existing statistics. The DBA_SQL_PROFILES
view is the best source for viewing the SQL profile name, attributes, and associated SQL text.
To view the internal SQL profile hint-related information, you can additionally query the DBMSHSXP_SQL_PROFILE_ATTR
view—for example:
SELECT
a.name
,b.comp_data
FROM dba_sql_profiles a
,dbmshsxp_sql_profile_attr b
WHERE a.name = b.profile_name;
Here is some sample output:
SYS_SQLPROF_0130520c90dc0002
<outline_data><hint><![CDATA[OPT_ESTIMATE(@"SEL$2",
NLJ_INDEX_SCAN, "FS"@"SEL$2", ("MAP"@"SEL$2"), "DB_FEAT_OPT_112_SUM_MV_IDX3",
SCALE_ROWS=0.3369001041)]]></h
The prior output gives you an indication of the types of hints within a SQL profile. This information is used by the optimizer to better estimate the cardinality of each execution step. This data allows the optimizer to make better decisions when generating an execution plan.
You can also view this internal SQL profile information by querying the SQLOBJ$
and SQLOBJ$DATA
views. The data in these views is in XML format, and therefore you must format the output with Oracle XML functions when querying—for example:
SELECT
extractvalue(value(a), '.') sqlprofile_hints
FROM sqlobj$ o
,sqlobj$data d
,table(xmlsequence(extract(xmltype(d.comp_data),'/outline_data/hint'))) a
WHERE o.name = '&&profile_name'
AND o. plan_id = d.plan_id
AND o.signature = d.signature
AND o.category = d.category
AND o.obj_type = d.obj_type;
Here is a small sample of the output:
OPT_ESTIMATE(@"SEL$EF0E05FC", INDEX_SCAN, "MGMT_TARGETS"@"SEL$4",
"MIDX3", SCALE_ROWS=50.68489486)
OPT_ESTIMATE(@"SEL$EF0E05FC", NLJ_INDEX_FILTER,
"MGMT_ECM_GEN_SNAPSHOT"@"SEL$3", ("MGMT_TARGETS"@"SEL$4"),
"IDX$$_1197C0001", SCALE_ROWS=0.4308705)
Again, these profile statistics don't force the optimizer to use a certain execution plan. Rather these statistics provide the optimizer the flexibility to choose a more efficient execution plan.
Note If you're using Oracle Database 10g, then use the SQLPROF$
and SQLPROF$ATTR
views.
You think that a SQL profile is no longer required for a query. You want to manually disable (not drop) the SQL profile.
First verify the name of the SQL profile that you want to disable:
SQL> select name, status from dba_sql_profiles;
Here's a partial snippet of the output:
NAME STATUS
------------------------------ --------
SYS_SQLPROF_012eda58a1be0001 ENABLED
Now use the DBMS_SQLTUNE.ALTER_SQL_PROFILE
procedure to modify the status of the profile to disabled:
BEGIN
DBMS_SQLTUNE.ALTER_SQL_PROFILE(
name => 'SYS_SQLPROF_012eda58a1be0001',
attribute_name => 'STATUS',
value => 'DISABLED'),
END;
/
The status of a SQL profile is one of several modifiable attributes. You can also modify characteristics such as the name, description, and category. See Table 12-3 for a description of the modifiable attributes.
Note You need the ALTER ANY SQL PROFILE
privilege to alter a SQL profile.
The category of a SQL profile has some interesting implications. A category allows you to control the use of a SQL profile through the setting of the SQLTUNE_CATEGORY
initialization parameter (this parameter can be set at the session or system level). When a query is executed, if a SQL profile is available, the optimizer will check to ensure that the category assigned to the SQL profile is the same as the system- or session-level setting of SQLTUNE_CATEGORY
. If the category of the SQL profile matches the setting of SQLTUNE_CATEGORY
, then the optimizer will consider using the SQL profile.
The default category for a SQL profile is DEFAULT
. Also the default value for SQLTUNE_CATEGORY
is DEFAULT
. Therefore, unless you alter the SQL profile category or modify the SQLTUNE_CATEGORY
parameter, the SQL profile will be used as input by the optimizer.
You can alter the category to something other than DEFAULT
. This means that only sessions that modify the initialization parameter of SQLTUNE_CATEGORY
to the value of the category for the SQL profile will be able to use the profile. For example, say you modify the SQL profile to have a category of TEST1
:
BEGIN
DBMS_SQLTUNE.ALTER_SQL_PROFILE(
name => 'SYS_SQLPROF_012eda58a1be0001',
attribute_name => 'CATEGORY',
value => 'TEST1'),
END;
/
Now the only sessions that can see and use the profile are those that have SQLTUNE_CATEGORY
set to TEST1
:
SQL> alter session set sqltune_category=TEST1;
This allows you to isolate a profile's use to only those sessions that have the SQLTUNE_CATEGORY
set to match the category of the SQL profile. This allows you to test the impact of implementing a SQL profile and back it out quickly, simply by altering either the session-level or system-level setting of SQLTUNE_CATEGORY
.
You can also manage many aspects of a SQL profile from Enterprise Manager. From the main database page, navigate to the Server tab. In the Query Optimizer section, click the SQL Plan Control tab. You should be presented with a screen similar to Figure 12-3.
Figure 12-3. Managing SQL profiles
From this screen, you can manage features such as enabling, disabling, changing the category, and dropping a SQL profile.
You've tested a query with and without a SQL profile attached to the query. You determine the query performance is not significantly better with the SQL profile. You want to drop the SQL profile so you're not cluttering up the data dictionary with unnecessary and obsolete information.
Use the DBMS_SQLTUNE.DROP_SQL_PROFILE
procedure to drop a SQL profile. Pass in the name of the SQL profile you want to drop—for example:
SQL> exec dbms_sqltune.drop_sql_profile('SYS_SQLPROF_012edef0d0a70002'),
If successful, you should see the following:
PL/SQL procedure successfully completed.
It's fairly easy to drop a SQL profile. You might want to do this if you're cleaning up a database or if you want to remove profiles from a testing environment. If you're unsure of the SQL profile name, you can query DBA_SQL_PROFILES
for more information (see Recipe 12-3 for details).
Note You need the DROP ANY SQL PROFILE
privilege to drop a SQL profile.
If you want to drop all profiles in a database, you can use PL/SQL to loop through all profiles and drop them:
declare
cursor c1 is select name from dba_sql_profiles;
begin
for r1 in c1 loop
dbms_sqltune.drop_sql_profile(r1.name);
end loop;
end;
/
You have a test database and want to extract all of the SQL profiles from the test database and move them to a production database.
Listed next are the steps involved with transporting a SQL profile from one database to another:
These steps are detailed in the following subsections.
Use the DBMS_SQLTUNE.CREATE_STGTAB_SQLPROF
procedure to create the staging table. This example creates a table named PROF_STAGE
owned by the MV_MAINT
user:
BEGIN
dbms_sqltune.create_stgtab_sqlprof(
table_name => 'PROF_STAGE',
schema_name => 'MV_MAINT' );
END;
/
Use the DBMS_SQLTUNE.PACK_STGTAB_SQLPROF
procedure to populate the table created in step 1 with SQL profile information. This example populates the table with information regarding a specific SQL profile:
BEGIN
dbms_sqltune.pack_stgtab_sqlprof(
profile_name => 'SYS_SQLPROF_012edf84806e0004',
staging_table_name => 'PROF_STAGE',
staging_schema_owner => 'MV_MAINT' );
END;
/
Tip The PROFILE_NAME
parameter can include wildcard characters. For example, if you want to transport all SQL profiles in a database, you can use ‘%’ for the PROFILE_NAME
.
You can copy the table from one database to the other via Data Pump, the old exp
/imp
utilities, or by using a database link. This example creates a database link in the destination database and then copies the table from the source database:
create database link source_db
connect to mv_maint
identified by foo
using 'source_db';
Once the database link has been created, the table can be copied directly from the source with the CREATE TABLE...AS SELECT
statement:
SQL> create table PROF_STAGE as select * from PROF_STAGE@source_db;
Now in the destination database, unpack the table to load profile information into the database:
BEGIN
DBMS_SQLTUNE.UNPACK_STGTAB_SQLPROF(
replace => TRUE,
staging_table_name => 'PROF_STAGE'),
END;
/
If no profile name is specified, the default is the %
wildcard character (meaning all profiles in the table will be loaded into the destination database).
It's fairly easy to copy SQL profiles from one database to another. You simply have to create a special table to hold the profile information, then populate the table, copy the table to the destination database, and lastly unpack the table's contents. Table 12-4 describes all of the parameters for the profile packing procedure.
The DBMS_SQLTUNE.UNPACK_STGTAB_SQLPROF
procedure takes the same parameters as the packing procedure with an additional REPLACE
parameter. The REPLACE
parameter specifies whether to replace profiles if they already exist (can be TRUE
or FALSE
).
You want to automatically create plan baselines for every SQL query that repeatedly executes in your database.
Listed next are the steps for automatically creating plan baselines for SQL statements that execute more than once:
OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES
parameter to TRUE
(either at the session or system level).OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES
to FALSE
.This next example illustrates the process for adding a plan baseline (for a query) using the prior steps. First set the specified initialization parameter at the session level:
SQL> alter session set optimizer_capture_sql_plan_baselines=true;
Now a query is executed twice. Oracle will automatically create a plan baseline for a query that is run two or more times while the OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES
parameter is set to TRUE
:
SQL> select emp_id from emp where emp_id=3000;
SQL> select emp_id from emp where emp_id=3000;
Now set the initialization parameter back to FALSE
.
SQL> alter session set optimizer_capture_sql_plan_baselines=false;
The query now should have an entry in the DBA_SQL_PLAN_BASELINES
view showing that it has an enabled plan baseline associated with it—for example:
SELECT
sql_handle, plan_name, enabled, accepted,
created, optimizer_cost, sql_text
FROM dba_sql_plan_baselines;
Here is a partial listing of the output:
SQL_HANDLE PLAN_NAME ENA ACC...
-------------------- ------------------------------ --- ---...
SQL_790bd425fe4a0125 SQL_PLAN_7k2yn4rz4n095d8a279cc YES YES...
Enabling OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES
allows you to automatically capture plan baselines for queries running repeatedly (more than once) in your database. The “Solution” section described how to use this feature at the session level. You can also set the parameter so that all repeating queries in the database have plan baselines generated—for example:
SQL> alter system set optimizer_capture_sql_plan_baselines=true;
From this point, any query in the database that runs more than once will automatically have a plan baseline created for it. We wouldn't recommend that you do this in a production environment unless you have first carefully tested this feature and ensured that there will be no adverse side effects (from storing a plan baseline for every query). However, you may have a test environment where you want to purposely create a plan baseline for every SQL statement that is repeatedly run.
You can also manage the use of plan baselines from Enterprise Manager. From the main database page, navigate to the Server tab. In the Query Optimizer section, click the SQL Plan Control tab. Next, click the SQL Plan Baseline tab. You should see a screen similar to Figure 12-4.
Figure 12-4. Managing plan baselines
From this screen, you can manage tasks such as enabling, disabling, dropping, and evolving plan baselines.
You want to create a plan baseline for a specific SQL statement that you're currently executing.
The procedure for manually associating a plan baseline with a SQL statement is as follows:
SQL_ID
as input to the DBMS_SPM
package to create a plan baseline for the SQL statement.For example, suppose you have a SQL statement you've been working with such as the following:
SQL> select emp_id from emp where emp_id = 100;
Now query the V$SQL
view to determine the SQL_ID
for the query:
select
sql_id
,sql_text
from v$sql
where sql_text
like 'select emp_id from emp where emp_id = 100';
Here is a snippet of the output:
SQL_ID SQL_TEXT
------------- ------------------------------------------------------------
0qgmjf9krq285 select emp_id from emp where emp_id = 100
Now that the SQL_ID
has been identified, use it as input to the DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE
function to create a plan baseline for the given query—for example:
DECLARE
plan1 PLS_INTEGER;
BEGIN
plan1 := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(sql_id => '0qgmjf9krq285'),
END;
/
The query now should have an entry in the DBA_SQL_PLAN_BASELINES
view showing that it has an enabled plan baseline associated with it—for example:
SQL> select sql_handle, plan_name, sql_text from dba_sql_plan_baselines;
Here's a small snippet of the output:
SQL_HANDLE PLAN_NAME SQL_TEXT
------------------------- ---------------------------------------- ----------------------
SQL_f34ef255797c4713 SQL_PLAN_g6mrkapwrsjsmd8a279cc select emp_id.....
The “Solution” section described how to identify a single SQL statement for which you want to create a plan baseline (based on the SQL_ID
) using a query in the cursor cache. There are many methods for creating a plan baseline for a query, such as using the SQL text, schema, module, and so on. For example, next a plan baseline is loaded based on a partial SQL string:
DECLARE
plan1 PLS_INTEGER;
BEGIN
plan1 := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(
attribute_name => 'sql_text'
,attribute_value => 'select emp_id from emp%'),
END;
/
See Table 12-5 for details on input parameters available with the DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE
function.
Note See Recipe 12-9 for an example of how to create plan baselines for SQL statements contained in a SQL tuning set.
You have the following scenario:
In essence, you are upgrading and would prefer that the optimizer choose the same execution plans both before and after the upgrade. You don't want the upgrade to result in new plans that risk degrading performance.
To deal with this problem, use the most resource-intensive SQL queries in the AWR as candidates for the creation of plan baselines. This solution uses the technique of creating an AWR baseline. An AWR baseline is a snapshot of activity in the AWR designated by begin/end snapshot IDs. Listed next are the steps for creating and populating a SQL tuning set with high resource-consuming SQL statements found in an AWR baseline and then creating plan baselines for those queries:
DBMS_SPM
to create a plan baseline for each query contained in the SQL tuning set.Note You have a great deal of flexibility on how to populate a SQL tuning set with high resource-consuming queries in the AWR or memory. See Chapter 11 for complete details on working with SQL tuning sets.
The first step is to create an AWR baseline. For example, suppose you knew you had high-load queries running between two snapshots in your database. The following creates an AWR baseline using two snapshot IDs:
BEGIN
DBMS_WORKLOAD_REPOSITORY.create_baseline (
start_snap_id => 26632,
end_snap_id => 26635,
baseline_name => 'peak_baseline_apr15_11'),
END;
/
If you're unsure of the available snapshots in your database, you can run an AWR report or select the SNAP_ID
from DBA_HIST_SNAPSHOTS
:
select snap_id, begin_interval_time
from dba_hist_snapshot order by 1;
Now create a SQL tuning set. This next bit of code creates a tuning set named test1
:
BEGIN
dbms_sqltune.create_sqlset(
sqlset_name => 'test1'
,description => 'STS from AWR'),
END;
/
Now the SQL tuning set (created in step 2) is populated with any queries found within the AWR baseline (created in step 1):
DECLARE
base_cur dbms_sqltune.sqlset_cursor;
BEGIN
OPEN base_cur FOR
SELECT value(x)
FROM table(dbms_sqltune.select_workload_repository(
'peak_baseline_apr15_11', null, null,'elapsed_time',
null, null, null, 15)) x;
--
dbms_sqltune.load_sqlset(
sqlset_name => 'test1',
populate_cursor => base_cur);
END;
/
In the prior lines of a code, the AWR baseline name is passed to the DBMS_SQLTUNE
package. The queries within the baseline are select by the elapsed time
, and the top 15 are specified.
Now the tuning set (populated in step 3) is provided as input to the DBMS_SPM
package:
DECLARE
test_plan1 PLS_INTEGER;
BEGIN
test_plan1 := dbms_spm.load_plans_from_sqlset(
sqlset_name=>'test1'),
END;
/
Any queries contained in the tuning set should now have entries in the DBA_SQL_PLAN_BASELINES
view.
The technique shown in the “Solution” section is a very powerful method for creating plan baselines for the most resource-consuming queries running in your database. The key to this recipe is understanding that you can use as input (to the DBMS_SPM
package) queries contained in a SQL tuning set. A SQL tuning set can be populated from high resource-consuming statements found in the AWR and memory. This allows you to easily create plan baselines for the most problematic queries.
Having plan baselines in place for resource-intensive queries helps ensure that the same execution plan is used after there are changes to your system, such as a database upgrades, changes in statistics, different data sets, and so on.
Keep in mind that it's possible to have more than one accepted execution plan within the plan baseline. If you have a specific plan that you want the optimizer to always use, then consider altering the plan to a FIXED
state. See Recipe 12-10 for details on altering a plan baseline to a FIXED
state.
You have several accepted plan baseline execution plans for one query. You want to specifically instruct the optimizer to give preference to one of the accepted plans.
The optimizer will give preference to plan baselines with a FIXED
state. Use the DBMS_SPM
package and ALTER_SQL_PLAN_BASELINE
function to alter a current plan baseline execution plan to FIXED
. Here's an example:
DECLARE
pf PLS_INTEGER;
BEGIN
pf := dbms_spm.alter_sql_plan_baseline(
plan_name => 'SQL_PLAN_1wskqhvrwf8g60e23be79'
,attribute_name => 'fixed'
,attribute_value => 'YES'),
END;
/
You can query the FIXED
column of DBA_SQL_PLAN_BASELINES
to verify that it is now baseline-neutered. Listed next is such a query:
SELECT
sql_handle, plan_name, enabled, accepted, fixed
FROM dba_sql_plan_baselines;
Here is some sample output:
SQL_HANDLE PLAN_NAME ENA ACC FIX
-------------------- ------------------------------ --- --- ---
SQL_457bf2f82571bd38 SQL_PLAN_4ayzkz0kr3g9s90e466fd YES YES NO
SQL_790bd425fe4a0125 SQL_PLAN_7k2yn4rz4n095d8a279cc YES YES YES
You can think of fixing a plan baseline as a way of establishing a preference hierarchy for how the optimizer chooses a plan baseline. The optimizer will give first priority to any accepted and fixed plan baselines. If none is available, then accepted non-fixed plan baselines are considered. Execution plans that are added to a plan baseline that already contains a fixed plan baseline will be considered secondary (unless you add them as fixed).
Table 12-6 describes the parameters available with ALTER_SQL_PLAN_BASELINE
. You can specify either the SQL_HANDLE
or PLAN_NAME
or both. If the SQL_HANDLE
is NULL
, then a PLAN_NAME
must be specified and vice versa.
The ATTRIBUTE_NAME
and ATTRIBUTE_VALUE
parameters consist of a name/value pairing that can be used to alter various attributes of a plan baseline. See Table 12-7 for a complete description of the possible pairings.
Tip Use the ENABLED
attribute of ALTER_SQL_PLAN_BASELINE
to either disable or re-enable a plan baseline for use.
You recently implemented a plan baseline for a query. You want to verify the configuration of a plan baseline.
Run the following query to view details regarding any plan baselines that have been configured:
set pages 100
set linesize 132
col sql_handle form a20
col plan_name form a30
col sql_text form a20
col created form a20
--
SELECT sql_handle, plan_name, enabled
,accepted, created, optimizer_cost, sql_text
FROM dba_sql_plan_baselines;
The output from the prior query is very wide and has been modified to fit within the page width:
SQL_HANDLE PLAN_NAME ENA ACC
-------------------- ------------------------------ --- ---
SQL_b98d2ae2145eec3d SQL_PLAN_bm39aw8a5xv1xae72d2f5 YES YES
CREATED OPTIMIZER_COST SQL_TEXT
-------------------- -------------- --------------------
21-MAR-11 10.53.29.0 2 select last_name from custs...
In the output, there are two key columns: the SQL_HANDLE
and PLAN_NAME
. Each query has an associated SQL_HANDLE
that is an identifier for a query. Each execution plan has a unique PLAN_NAME
. The PLAN_NAME
will be unique within DBA_SQL_PLAN_BASELINES
, whereas there could be multiple rows with the same SQL_HANDLE
(but different PLAN_NAME
).
The DBA_SQL_PLAN_BASELINES
view provides a quick and easy way to determine if plan baselines exist and are in use. If a plan is enabled and accepted, then the query has a plan baseline in use.
Note There is no ALL
or USER
-level data dictionary views for plan baselines. This is because the plan baseline is associated with a specific SQL statement and not a user.
If you have doubts about whether a plan baseline is being considered by the optimizer, then set AUTOTRACE
on and view the output—for example:
SQL> set autotrace trace explain;
SQL> select emp_id from emp where emp_id = 100;
Here is a partial listing of the output indicating that a SQL plan baseline execution plan is used for this query:
Execution Plan
----------------------------------------------------------
Plan hash value: 2872589290
--------------------------------------------------------------------------
..................
- SQL plan baseline "SQL_PLAN_g6mrkapwrsjsmd8a279cc" used for this statement
You want to quickly view details regarding an existing plan baseline, such as the associated execution plan.
Use the DBMS_XPLAN.DISPLAY_SQL_PLAN_BASELINE
function to display the execution plan and corresponding plan baseline details. This example reports details for a specific plan:
SELECT *
FROM TABLE(
DBMS_XPLAN.DISPLAY_SQL_PLAN_BASELINE(plan_name=>'SQL_PLAN_bm39aw8a5xv1xae72d2f5'));
Here is some sample output:
--------------------------------------------------------------------------------
SQL handle: SQL_b98d2ae2145eec3d
SQL text: select last_name from custs where last_name='DAVIS'
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Plan name: SQL_PLAN_bm39aw8a5xv1xae72d2f5 Plan id: 2926760693
Enabled: YES Fixed: NO Accepted: YES Origin: MANUAL-LOAD
--------------------------------------------------------------------------------
Plan hash value: 1824334906
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 54 | 2 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| CUSTS | 2 | 54 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------
The DBMS_XPLAN.DISPLAY_SQL_PLAN_BASELINE
function allows you to display one or more execution plans in a plan baseline. The return type for this function is a PL/SQL table type. This function takes three parameters (described in Table 12-8).
If you want to display all plans for a SQL statement, then use as input the SQL_HANDLE
parameter—for example:
SELECT *
FROM TABLE(
DBMS_XPLAN.DISPLAY_SQL_PLAN_BASELINE(sql_handle=>'SQL_b98d2ae2145eec3d'));
Here is a partial listing of the output showing there are multiple plans in the plan baseline for this SQL query:
--------------------------------------------------------------------------------
Plan name: SQL_PLAN_bm39aw8a5xv1x519fc7bf Plan id: 1369425855
Enabled: YES Fixed: NO Accepted: NO Origin: AUTO-CAPTURE
--------------------------------------------------------------------------------
Plan hash value: 16205770
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 54 | 1 (0)| 00:00:01 |
|* 1 | INDEX RANGE SCAN| CUSTS_IDX1 | 2 | 54 | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("LAST_NAME"='DAVIS')
--------------------------------------------------------------------------------
Plan name: SQL_PLAN_bm39aw8a5xv1xae72d2f5 Plan id: 2926760693
Enabled: YES Fixed: NO Accepted: YES Origin: MANUAL-LOAD
--------------------------------------------------------------------------------
Plan hash value: 1824334906
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 54 | 2 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| CUSTS | 2 | 54 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------
You have the following scenario:
DBA_SQL_PLAN_BASELINES
view.You want to evolve the low-cost plan in the history so that it's moved to an accepted plan in the baseline. You realize that once the plan is accepted in the baseline, the optimizer will use it (if it's the lowest-cost plan in the baseline).
First verify that there are plans in the unaccepted state for the query in question (see Recipes 12-11 and 12-12 for more details). Here's a quick example:
SELECT sql_handle, plan_name, enabled, accepted, optimizer_cost
FROM dba_sql_plan_baselines
WHERE sql_text like '%select emp_id from emp where emp_id = 100%';
Here is the output indicating there are two plans, one unaccepted but with a much lower cost:
SQL_HANDLE PLAN_NAME ENA ACC OPTIMIZER_COST
-------------------- ------------------------------ --- --- --------------
SQL_f34ef255797c4713 SQL_PLAN_g6mrkapwrsjsm01205c23 YES NO 1
SQL_f34ef255797c4713 SQL_PLAN_g6mrkapwrsjsmd8a279cc YES YES 7
Use the DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE
function to move a plan from the history to the baseline (evolve the plan). In this example, the SQL handle (unique SQL string associated with a SQL statement) is used to evolve a plan:
SET SERVEROUT ON SIZE 1000000
SET LONG 100000
DECLARE
rpt CLOB;
BEGIN
rpt := DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE(
sql_handle => 'SQL_f34ef255797c4713'),
DBMS_OUTPUT.PUT_LINE(rpt);
END;
/
If Oracle determines that there is an unaccepted plan with a lower cost, then you'll see output similar to this indicating that the plan has been moved to the accepted state (evolved):
-------------------------------------------------------------------------------
Evolve SQL Plan Baseline
Report
-------------------------------------------------------------------------------
Inputs:
-------
SQL_HANDLE =
SQL_f34ef255797c4713
PLAN_NAME =
TIME_LIMIT = DBMS_SPM.AUTO_LIMIT
VERIFY = YES
COMMIT = YES
Plan:
SQL_PLAN_4fpttm0b55uwr918dd295
------------------------------------
Plan was verified: Time used .09 seconds.
Plan passed
performance criterion: 11.56 times better than baseline plan.
Plan was changed to an accepted plan.
You can quickly verify that the new plan baseline is now in use by setting AUTOTRACE
on and running the query—for example:
SQL> set autotrace trace explain;
SQL> select emp_id from emp where emp_id = 100;
Here's a small snippet of the output indicating the new plan baseline is in use:
SQL plan baseline "SQL_PLAN_g6mrkapwrsjsm01205c23" used for this statement
One key feature of SQL plan management is that when a new low-cost plan is generated by the query optimizer, if the new low-cost plan has a lower cost than the accepted plan(s) in the plan baseline, the new low-cost plan will automatically be added to the query's plan history in an unaccepted state.
You can choose to accept this new low-cost plan, which then moves it into the plan baseline as accepted. Moving an unaccepted execution plan from the plan history to the plan baseline (ENABLED
and ACCEPTED
) is known as evolving the plan baseline.
Why would a new plan ever be generated by the optimizer? There are several factors that would cause the optimizer to create a new execution plan that doesn't match an existing one in the plan baseline:
This gives you a powerful technique to manage and use new plans as they become available. You can use the DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE
function in the following modes:
Table 12-9 describes the parameters used in the DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE
function.
You're working with a test database that has many SQL statements with associated plan baselines. You want to determine what the performance difference would be without the plan baselines enabled and therefore want to temporarily disable the use of plan baselines.
To disable the use of any SQL plan baselines within the database, set the OPTIMIZER_USE_SQL_PLAN_BASELINES
initialization parameter to FALSE
:
SQL> alter system set optimizer_use_sql_plan_baselines=false scope=both;
The prior line disables the use of the plan baselines at the SYSTEM
level and records the value in memory and in the server parameter file. To re-enable the use of plan baselines, set the value back to TRUE
.
You can also set the OPTIMIZER_USE_SQL_PLAN_BASELINES
at the session level. This disables the use of plan baselines for the duration of the session for the currently connected user:
SQL> alter session set optimizer_use_sql_plan_baselines=false;
The default value for OPTIMIZER_USE_SQL_PLAN_BASELINES
is TRUE
, which means by default, if plan baselines are available, they will be used. When enabled, the optimizer will look for a valid plan baseline execution plan for the given SQL query and choose the one with the lowest cost. This gives you a quick and easy way to disable/enable the use of plan baselines within your entire database or specific to a session.
If you want to disable the use of one specific plan baseline, then alter its state to DISABLED
:
DECLARE
pf PLS_INTEGER;
BEGIN
pf := dbms_spm.alter_sql_plan_baseline(
plan_name => 'SQL_PLAN_4ayzkz0kr3g9s6afbe2b3'
,attribute_name => 'ENABLED'
,attribute_value => 'NO'),
END;
/
Tip See Recipe 12-10 for more details on how to alter plan baselines.
You have several plan baselines that you no longer want to use and therefore want to remove them.
You can drop a single plan baseline. This removes a single plan baseline using the PLAN_NAME
parameter:
DECLARE
plan_name1 PLS_INTEGER;
BEGIN
plan_name1 := DBMS_SPM.DROP_SQL_PLAN_BASELINE(
plan_name => 'SQL_PLAN_bm39aw8a5xv1x519fc7bf'),
END;
/
You can also drop all plans associated with a SQL statement. This example removes all plans associated with a SQL statement using the SQL_HANDLE
parameter:
DECLARE
sql_handle1 PLS_INTEGER;
BEGIN
sql_handle1 := DBMS_SPM.DROP_SQL_PLAN_BASELINE(
sql_handle => 'SQL_b98d2ae2145eec3d'),
END;
/
You may occasionally want to remove SQL plan baselines for the following reasons:
As shown in the “Solution” section, you can remove a specific plan baseline via the PLAN_NAME
parameter. This will remove one specific plan. If you have several plans associated with one SQL statement, you can remove all plan baselines for that SQL statement via the SQL_HANDLE
parameter.
If you have a database where you want to clear out all plans, then you can encapsulate the call DBMS_SPM.DROP_SQL_PLAN_BASELINE
within a PL/SQL block that drops all plans by looping through any plan found in DBA_SQL_PLAN_BASELINES
:
SET SERVEROUT ON SIZE 1000000
DECLARE
sql_handle1 PLS_INTEGER;
CURSOR c1 IS
SELECT sql_handle
FROM dba_sql_plan_baselines;
BEGIN
FOR r1 IN c1 LOOP
sql_handle1 := DBMS_SPM.DROP_SQL_PLAN_BASELINE(sql_handle => r1.sql_handle);
DBMS_OUTPUT.PUT_LINE('PB dropped for SH: ' || r1.sql_handle);
END LOOP;
END;
/
You have a test environment, and you want to ensure that all of the plan baselines in the test system are moved to a production database.
Follow these steps to transport plan baselines:
DBMS_SPM
package and CREATE_STGTAB_BASELINE
procedure.DBMS_SPM.PACK_STGTAB_BASELINE
function.DBMS_SPM.UNPACK_STGTAB_BASELINE
function.This example first uses the DBMS_SPM
package to create a table named EXP_PB
:
BEGIN
DBMS_SPM.CREATE_STGTAB_BASELINE(table_name => 'exp_pb'),
END;
/
Note You cannot create the staging table in the SYS
user.
Next the EXP_PB
table is populated with plan baselines created by the database user MV_MAINT
:
DECLARE
pbs NUMBER;
BEGIN
pbs := DBMS_SPM.PACK_STGTAB_BASELINE(
table_name => 'exp_pb',
enabled => 'yes',
creator => 'MV_MAINT'),
END;
/
The prior code populates the table with all plan baselines created by a user. You can also populate the table by PLAN_NAME
, SQL_HANDLE
, SQL_TEXT
, or various other criteria. The only mandatory parameter is the name of the table to be populated.
Now copy the staging table to the destination database. You can use a database link, Data Pump, or the old exp
/imp
utilities to accomplish this.
Lastly, on the destination database, use the DBMS_SPM.UNPACK_STGTAB_BASELINE
function to take the contents of the EXP_PB
table and create plan baselines:
DECLARE
pbs NUMBER;
BEGIN
pbs := DBMS_SPM.UNPACK_STGTAB_BASELINE(
table_name => 'exp_pb',
enabled => 'yes'),
END;
/
You should now have all of the plan baselines transferred to your target database. You can query DBA_SQL_PLAN_BASELINES
to verify this.
It's a fairly easy process to create a table, populate it with plan baseline information, copy the table, and the import its contents into the destination database. As shown in step 2 of the “Solution” section of this recipe, the PACK_STGTAB_BASELINE
function is used (see Table 12-10). This function allows quite a bit of flexibility in what types of plan baselines you want exported. You can limit the plan baselines extracted to a specific user, or enabled, or accepted, and so on.
Likewise, the DBMS_SPM.UNPACK_STGTAB_BASELINE
function allows you a great deal of flexibility on what types of plan baselines are extracted from the staging table and loaded into the destination database. The input parameters for UNPACK_STGTAB_BASELINE
are the same as the parameters used for PACK_STGTAB_BASELINE
(described in Table 12-10).