Oracle SQL Profile指定执行计划
2016-06-22 16:57
447 查看
SQL Profiles在10g中引入,可以说是Outlines的进化。Outlines能够实现的功能SQL Profiles也完全能够实现,而SQL Profiles具有Outlines不具备的优化(更容易生成、更改和控制,在对SQL语句的支持上做得更好,也就是适用范围更广)
现在我在使用Outlines的场合,均使用SQL Profiles来替代。有一次准备对1条SQL语句使用Outline进行执行计划的稳定,结果使用Outline之后,系统出现大量的library cache latch的争用,不得不关闭Outline的使用,但是改用SQL Profiles不再有这个问题。这或许是个BUG,不过既然能用SQL Profiles代替,也就没再深入去研究这个问题。
使用SQL Profiles无非是两个目的:
1、锁定或者说是稳定执行计划。
2、在不能修改应用中的SQL的情况下使SQL语句按指定的执行计划运行。
SQL Profile就是为某一SQL语句提供除了系统统计信息、对象(表和索引等)统计信息之外的其他信息,比如运行环境、额外的更准确的统计信息,以帮助优化器为SQL语句选择更适合的执行计划。这些说法显得比较枯燥,还是来看看下面的测试:
首先建2个测试表:
SQL> create table t1 as select object_id,object_name from dba_objects where rownum<=50000;
SQL> create table t2 as select * from dba_objects;
SQL> create index t2_idx on t2(object_id);
SQL> exec dbms_stats.gather_table_stats('sys','t1',cascade=>true,method_opt=>'for all columns size 1');
SQL> exec dbms_stats.gather_table_stats('sys','t2',cascade=>true,method_opt=>'for all columns size 1');
然后看看下面这一条SQL:
SQL> set autotrace traceonly
SQL> select t1.*,t2.owner from t1,t2 where t1.object_name like '%T1%' and t1.object_id=t2.object_id;
36 rows selected.
Elapsed: 00:00:00.06
Execution Plan
----------------------------------------------------------
Plan hash value: 1838229974
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2500 | 100K| 412 (1)| 00:00:05 |
|* 1 | HASH JOIN | | 2500 | 100K| 412 (1)| 00:00:05 |
|* 2 | TABLE ACCESS FULL| T1 | 2500 | 75000 | 67 (2)| 00:00:01 |
| 3 | TABLE ACCESS FULL| T2 | 87908 | 944K| 344 (1)| 00:00:05 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
2 - filter("T1"."OBJECT_NAME" LIKE '%T1%' AND "T1"."OBJECT_NAME" IS
NOT NULL)
Statistics
----------------------------------------------------------
38 recursive calls
0 db block gets
1557 consistent gets
0 physical reads
0 redo size
2132 bytes sent via SQL*Net to client
542 bytes received via SQL*Net from client
4 SQL*Net roundtrips to/from client
11 sorts (memory)
0 sorts (disk)
36 rows processed
注意,逻辑读为1557
从执行计划可以看到,这条SQL语句在2个表上都是全表扫描。在第1个表T1上,有 like '%T1%'这样的条件,导致只能全表扫描,这没有问题。但是第2个表,也是全表扫描,这里有没有问题呢?这里的问题在于执行计划ID=1的那一行,Oracle优化器评估T1 like '%T1%'返回的结果行数为2498行,即T1表总行数的5%,如果2个表采用index range scan+nested loop连接,oracle评估的成本会高于full table scan+hash join。下面可以看到Oracle优化器评估的index
range_scan+nested loop的成本:
SQL> select /*+ use_nl(t1 t2) index(t2) */ t1.*,t2.owner from t1,t2 where t1.object_name like '%T1%' and t1.object_id=t2.object_id;
36 rows selected.
Elapsed: 00:00:00.04
Execution Plan
----------------------------------------------------------
Plan hash value: 1022743391
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2500 | 100K| 5070 (1)| 00:01:01 |
| 1 | NESTED LOOPS | | 2500 | 100K| 5070 (1)| 00:01:01 |
| 2 | NESTED LOOPS | | 2500 | 100K| 5070 (1)| 00:01:01 |
|* 3 | TABLE ACCESS FULL | T1 | 2500 | 75000 | 67 (2)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | T2_IDX | 1 | | 1 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 11 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("T1"."OBJECT_NAME" LIKE '%T1%' AND "T1"."OBJECT_NAME" IS NOT
NULL)
4 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
307 consistent gets
0 physical reads
0 redo size
2132 bytes sent via SQL*Net to client
542 bytes received via SQL*Net from client
4 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
36 rows processed
从执行计划可以看到Oracle优化器评估的成本为5070,远远高于原来的412。
但是实际的逻辑读是多少呢?从1557降到307。所以这里可以看出来,由于Oracle优化器过高地估计了T1表经过like操作过滤返回的行数,也就过高地估计了nest loop的成本,最终也就选择了不是最优的执行计划。
【接下来开始使用SQL_PROFILE进行优化】
先取出没有加hint的那条SQL_ID。
SQL> select sql_id,sql_text from v$sqlarea where sql_text like 'select t1.*,t2.owner from t1,t2 where t1.object_name%';
SQL_ID SQL_TEXT
------------- ----------------------------------------------------------------------------------------------------
4zbqykx89yc8v select t1.*,t2.owner from t1,t2 where t1.object_name like '%T1%' and t1.object_id=t2.object_id
下面我们用Oracle的SQL Tuning Advisor来尝试这条SQL:
SQL> var tuning_task varchar2(100);
SQL> DECLARE
2 l_sql_id v$session.prev_sql_id%TYPE;
3 l_tuning_task VARCHAR2(30);
4 BEGIN
5 l_sql_id:='4zbqykx89yc8v'; --获取上一条SQL的SQL_ID,即没有加hint的那条SQL
6 l_tuning_task := dbms_sqltune.create_tuning_task(sql_id => l_sql_id);
7 :tuning_task:=l_tuning_task;
8 dbms_sqltune.execute_tuning_task(l_tuning_task);
9 dbms_output.put_line(l_tuning_task);
10 END;
11 /
PL/SQL procedure successfully completed.
SQL> print tuning_task;
TUNING_TASK
--------------------------------------------------------------------------------------------------------------------------------
TASK_10619
SQL> set long 99999
SQL> col comments for a100
SQL> SELECT dbms_sqltune.report_tuning_task(:tuning_task)COMMENTS FROM dual;
COMMENTS
----------------------------------------------------------------------------------------------------
GENERAL INFORMATION SECTION
-------------------------------------------------------------------------------
Tuning Task Name : TASK_10684
Tuning Task Owner : SYS
Workload Type : Single SQL Statement
Scope : COMPREHENSIVE
Time Limit(seconds): 1800
Completion Status : COMPLETED
Started at : 04/06/2016 14:20:15
Completed at : 04/06/2016 14:20:18
COMMENTS
----------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Schema Name: SYS
SQL ID : 4zbqykx89yc8v
SQL Text : select t1.*,t2.owner from t1,t2 where t1.object_name like '%T1%'
and t1.object_id=t2.object_id
-------------------------------------------------------------------------------
FINDINGS SECTION (1 finding)
-------------------------------------------------------------------------------
1- SQL Profile
ca58
Finding (see explain plans section below)
COMMENTS
----------------------------------------------------------------------------------------------------
--------------------------------------------------------
A potentially better execution plan was found for this statement.
Recommendation (estimated benefit: 80.04%)
------------------------------------------
- Consider accepting the recommended SQL profile.
execute dbms_sqltune.accept_sql_profile(task_name => 'TASK_10684',
task_owner => 'SYS', replace => TRUE);
Validation results
------------------
COMMENTS
----------------------------------------------------------------------------------------------------
The SQL profile was tested by executing both its plan and the original plan
and measuring their respective execution statistics. A plan may have been
only partially executed if the other could be run to completion in less time.
Original Plan With SQL Profile % Improved
------------- ---------------- ----------
Completion Status: COMPLETE COMPLETE
Elapsed Time (s): .036642 .02716 25.87 %
CPU Time (s): .036494 .026995 26.02 %
User I/O Time (s): 0 0
Buffer Gets: 1503 298 80.17 %
COMMENTS
----------------------------------------------------------------------------------------------------
Physical Read Requests: 0 0
Physical Write Requests: 0 0
Physical Read Bytes: 0 0
Physical Write Bytes: 0 0
Rows Processed: 36 36
Fetches: 36 36
Executions: 1 1
Notes
-----
1. Statistics for the original plan were averaged over 10 executions.
COMMENTS
----------------------------------------------------------------------------------------------------
2. Statistics for the SQL profile plan were averaged over 10 executions.
-------------------------------------------------------------------------------
EXPLAIN PLANS SECTION
-------------------------------------------------------------------------------
1- Original With Adjusted Cost
------------------------------
Plan hash value: 1838229974
---------------------------------------------------------------------------
COMMENTS
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 36 | 1476 | 412 (1)| 00:00:05 |
|* 1 | HASH JOIN | | 36 | 1476 | 412 (1)| 00:00:05 |
|* 2 | TABLE ACCESS FULL| T1 | 36 | 1080 | 67 (2)| 00:00:01 |
| 3 | TABLE ACCESS FULL| T2 | 87908 | 944K| 344 (1)| 00:00:05 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
COMMENTS
----------------------------------------------------------------------------------------------------
1 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
2 - filter("T1"."OBJECT_NAME" LIKE '%T1%' AND "T1"."OBJECT_NAME" IS
NOT NULL)
2- Using SQL Profile
--------------------
Plan hash value: 1022743391
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
COMMENTS
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 36 | 1476 | 139 (1)| 00:00:02 |
| 1 | NESTED LOOPS | | 36 | 1476 | 139 (1)| 00:00:02 |
| 2 | NESTED LOOPS | | 36 | 1476 | 139 (1)| 00:00:02 |
|* 3 | TABLE ACCESS FULL | T1 | 36 | 1080 | 67 (2)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | T2_IDX | 1 | | 1 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 11 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
COMMENTS
----------------------------------------------------------------------------------------------------
3 - filter("T1"."OBJECT_NAME" LIKE '%T1%' AND "T1"."OBJECT_NAME" IS NOT
NULL)
4 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
-------------------------------------------------------------------------------
SQL>
通过结果对比,显然加了HINT的方式效率更高。上面代码中的sql_id是从v$sql来,对应的是没有加hint的SQL。
结果看起来非常棒,SQL Tuning Advisor为我们找到了理想的执行计划,T1表上经过谓词过滤后返回的行数评估为36,相当地精确。我们要做的就是Accept SQL Profile,接受这个SQL Profile。这样一来,即使SQL正文不加hint该SQL也会按照我们的要求走。注意:执行计划的NOTE部分,有个SQL_PROFILE字样
SQL> execute dbms_sqltune.accept_sql_profile(task_name => :tuning_task,replace => TRUE,force_match=>true);
SQL> set autotrace traceonly
SQL> select t1.*,t2.owner from t1,t2 where t1.object_name like '%T1%' and t1.object_id=t2.object_id;
36 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1022743391
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 36 | 1476 | 139 (1)| 00:00:02 |
| 1 | NESTED LOOPS | | 36 | 1476 | 139 (1)| 00:00:02 |
| 2 | NESTED LOOPS | | 36 | 1476 | 139 (1)| 00:00:02 |
|* 3 | TABLE ACCESS FULL | T1 | 36 | 1080 | 67 (2)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | T2_IDX | 1 | | 1 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 11 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("T1"."OBJECT_NAME" LIKE '%T1%' AND "T1"."OBJECT_NAME" IS NOT
NULL)
4 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
Note
-----
- SQL profile "SYS_SQLPROF_0153ea63e5ff0000" used for this statement
Statistics
----------------------------------------------------------
34 recursive calls
0 db block gets
320 consistent gets
1 physical reads
144 redo size
2132 bytes sent via SQL*Net to client
541 bytes received via SQL*Net from client
4 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
36 rows processed
SQL>
我们再执行其他的类似SQL看看:比如修改like条件,再加几个空格修改大小写等
SQL> SElect t1.*,t2.owner from t1,t2 where t1.object_name like '%T2%' and t1.object_id=t2.object_ID;
57 rows selected.
Elapsed: 00:00:00.35
Execution Plan
----------------------------------------------------------
Plan hash value: 1022743391
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 36 | 1476 | 139 (1)| 00:00:02 |
| 1 | NESTED LOOPS | | 36 | 1476 | 139 (1)| 00:00:02 |
| 2 | NESTED LOOPS | | 36 | 1476 | 139 (1)| 00:00:02 |
|* 3 | TABLE ACCESS FULL | T1 | 36 | 1080 | 67 (2)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | T2_IDX | 1 | | 1 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 11 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("T1"."OBJECT_NAME" LIKE '%T2%' AND "T1"."OBJECT_NAME" IS NOT
NULL)
4 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
Note
-----
- SQL profile "SYS_SQLPROF_0153ea63e5ff0000" used for this statement
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
300 consistent gets
7 physical reads
0 redo size
2957 bytes sent via SQL*Net to client
552 bytes received via SQL*Net from client
5 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
57 rows processed
SQL>
卧槽,居然还是有效地!调用了同一个SQL Profile!即便cursor_sharing是EXACT的。因为force_match参数设为TRUE,那么dba_sql_profiles中的signature则是由SQL的force_matching_signature而来,否则便是exact_matching_signature。对于Outlines来说,则只能是exact_matching_signature。从这个角度上讲,Sql Profiles比Outlines的使用范围更广,因为Sql profiles对没有使用绑定变量的SQL也支持得很好。
SQL> show parameter cursor_sharing
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
cursor_sharing string EXACT
补充:对SQL语句,去掉重复的空格(不包括字符常量),将大小写转换成相同,比如均为大写(不包括字符常量)后,如果SQL相同,那么SQL语句的exact_matching_signature就是相同的。对SQL语句,去掉重复的空格(不包括字符常量),将大小写转换成相同,比如均为大写(不包括字符常量),然后去掉SQL中的常量,如果SQL相同,那么SQL语句的force_matching_signature就是相同的。但是例外的情况是:如果SQL中有绑定变量,force_matching_signature就会与exact_matching_signature一样的生成标准。
7、删除profile
如不再使用,可删除profile
SQL> exec dbms_sqltune.drop_sql_profile('SYS_SQLPROF_015431a0d02b0000');
现在我在使用Outlines的场合,均使用SQL Profiles来替代。有一次准备对1条SQL语句使用Outline进行执行计划的稳定,结果使用Outline之后,系统出现大量的library cache latch的争用,不得不关闭Outline的使用,但是改用SQL Profiles不再有这个问题。这或许是个BUG,不过既然能用SQL Profiles代替,也就没再深入去研究这个问题。
使用SQL Profiles无非是两个目的:
1、锁定或者说是稳定执行计划。
2、在不能修改应用中的SQL的情况下使SQL语句按指定的执行计划运行。
SQL Profile就是为某一SQL语句提供除了系统统计信息、对象(表和索引等)统计信息之外的其他信息,比如运行环境、额外的更准确的统计信息,以帮助优化器为SQL语句选择更适合的执行计划。这些说法显得比较枯燥,还是来看看下面的测试:
首先建2个测试表:
SQL> create table t1 as select object_id,object_name from dba_objects where rownum<=50000;
SQL> create table t2 as select * from dba_objects;
SQL> create index t2_idx on t2(object_id);
SQL> exec dbms_stats.gather_table_stats('sys','t1',cascade=>true,method_opt=>'for all columns size 1');
SQL> exec dbms_stats.gather_table_stats('sys','t2',cascade=>true,method_opt=>'for all columns size 1');
然后看看下面这一条SQL:
SQL> set autotrace traceonly
SQL> select t1.*,t2.owner from t1,t2 where t1.object_name like '%T1%' and t1.object_id=t2.object_id;
36 rows selected.
Elapsed: 00:00:00.06
Execution Plan
----------------------------------------------------------
Plan hash value: 1838229974
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2500 | 100K| 412 (1)| 00:00:05 |
|* 1 | HASH JOIN | | 2500 | 100K| 412 (1)| 00:00:05 |
|* 2 | TABLE ACCESS FULL| T1 | 2500 | 75000 | 67 (2)| 00:00:01 |
| 3 | TABLE ACCESS FULL| T2 | 87908 | 944K| 344 (1)| 00:00:05 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
2 - filter("T1"."OBJECT_NAME" LIKE '%T1%' AND "T1"."OBJECT_NAME" IS
NOT NULL)
Statistics
----------------------------------------------------------
38 recursive calls
0 db block gets
1557 consistent gets
0 physical reads
0 redo size
2132 bytes sent via SQL*Net to client
542 bytes received via SQL*Net from client
4 SQL*Net roundtrips to/from client
11 sorts (memory)
0 sorts (disk)
36 rows processed
注意,逻辑读为1557
从执行计划可以看到,这条SQL语句在2个表上都是全表扫描。在第1个表T1上,有 like '%T1%'这样的条件,导致只能全表扫描,这没有问题。但是第2个表,也是全表扫描,这里有没有问题呢?这里的问题在于执行计划ID=1的那一行,Oracle优化器评估T1 like '%T1%'返回的结果行数为2498行,即T1表总行数的5%,如果2个表采用index range scan+nested loop连接,oracle评估的成本会高于full table scan+hash join。下面可以看到Oracle优化器评估的index
range_scan+nested loop的成本:
SQL> select /*+ use_nl(t1 t2) index(t2) */ t1.*,t2.owner from t1,t2 where t1.object_name like '%T1%' and t1.object_id=t2.object_id;
36 rows selected.
Elapsed: 00:00:00.04
Execution Plan
----------------------------------------------------------
Plan hash value: 1022743391
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2500 | 100K| 5070 (1)| 00:01:01 |
| 1 | NESTED LOOPS | | 2500 | 100K| 5070 (1)| 00:01:01 |
| 2 | NESTED LOOPS | | 2500 | 100K| 5070 (1)| 00:01:01 |
|* 3 | TABLE ACCESS FULL | T1 | 2500 | 75000 | 67 (2)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | T2_IDX | 1 | | 1 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 11 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("T1"."OBJECT_NAME" LIKE '%T1%' AND "T1"."OBJECT_NAME" IS NOT
NULL)
4 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
307 consistent gets
0 physical reads
0 redo size
2132 bytes sent via SQL*Net to client
542 bytes received via SQL*Net from client
4 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
36 rows processed
从执行计划可以看到Oracle优化器评估的成本为5070,远远高于原来的412。
但是实际的逻辑读是多少呢?从1557降到307。所以这里可以看出来,由于Oracle优化器过高地估计了T1表经过like操作过滤返回的行数,也就过高地估计了nest loop的成本,最终也就选择了不是最优的执行计划。
【接下来开始使用SQL_PROFILE进行优化】
先取出没有加hint的那条SQL_ID。
SQL> select sql_id,sql_text from v$sqlarea where sql_text like 'select t1.*,t2.owner from t1,t2 where t1.object_name%';
SQL_ID SQL_TEXT
------------- ----------------------------------------------------------------------------------------------------
4zbqykx89yc8v select t1.*,t2.owner from t1,t2 where t1.object_name like '%T1%' and t1.object_id=t2.object_id
下面我们用Oracle的SQL Tuning Advisor来尝试这条SQL:
SQL> var tuning_task varchar2(100);
SQL> DECLARE
2 l_sql_id v$session.prev_sql_id%TYPE;
3 l_tuning_task VARCHAR2(30);
4 BEGIN
5 l_sql_id:='4zbqykx89yc8v'; --获取上一条SQL的SQL_ID,即没有加hint的那条SQL
6 l_tuning_task := dbms_sqltune.create_tuning_task(sql_id => l_sql_id);
7 :tuning_task:=l_tuning_task;
8 dbms_sqltune.execute_tuning_task(l_tuning_task);
9 dbms_output.put_line(l_tuning_task);
10 END;
11 /
PL/SQL procedure successfully completed.
SQL> print tuning_task;
TUNING_TASK
--------------------------------------------------------------------------------------------------------------------------------
TASK_10619
SQL> set long 99999
SQL> col comments for a100
SQL> SELECT dbms_sqltune.report_tuning_task(:tuning_task)COMMENTS FROM dual;
COMMENTS
----------------------------------------------------------------------------------------------------
GENERAL INFORMATION SECTION
-------------------------------------------------------------------------------
Tuning Task Name : TASK_10684
Tuning Task Owner : SYS
Workload Type : Single SQL Statement
Scope : COMPREHENSIVE
Time Limit(seconds): 1800
Completion Status : COMPLETED
Started at : 04/06/2016 14:20:15
Completed at : 04/06/2016 14:20:18
COMMENTS
----------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Schema Name: SYS
SQL ID : 4zbqykx89yc8v
SQL Text : select t1.*,t2.owner from t1,t2 where t1.object_name like '%T1%'
and t1.object_id=t2.object_id
-------------------------------------------------------------------------------
FINDINGS SECTION (1 finding)
-------------------------------------------------------------------------------
1- SQL Profile
ca58
Finding (see explain plans section below)
COMMENTS
----------------------------------------------------------------------------------------------------
--------------------------------------------------------
A potentially better execution plan was found for this statement.
Recommendation (estimated benefit: 80.04%)
------------------------------------------
- Consider accepting the recommended SQL profile.
execute dbms_sqltune.accept_sql_profile(task_name => 'TASK_10684',
task_owner => 'SYS', replace => TRUE);
Validation results
------------------
COMMENTS
----------------------------------------------------------------------------------------------------
The SQL profile was tested by executing both its plan and the original plan
and measuring their respective execution statistics. A plan may have been
only partially executed if the other could be run to completion in less time.
Original Plan With SQL Profile % Improved
------------- ---------------- ----------
Completion Status: COMPLETE COMPLETE
Elapsed Time (s): .036642 .02716 25.87 %
CPU Time (s): .036494 .026995 26.02 %
User I/O Time (s): 0 0
Buffer Gets: 1503 298 80.17 %
COMMENTS
----------------------------------------------------------------------------------------------------
Physical Read Requests: 0 0
Physical Write Requests: 0 0
Physical Read Bytes: 0 0
Physical Write Bytes: 0 0
Rows Processed: 36 36
Fetches: 36 36
Executions: 1 1
Notes
-----
1. Statistics for the original plan were averaged over 10 executions.
COMMENTS
----------------------------------------------------------------------------------------------------
2. Statistics for the SQL profile plan were averaged over 10 executions.
-------------------------------------------------------------------------------
EXPLAIN PLANS SECTION
-------------------------------------------------------------------------------
1- Original With Adjusted Cost
------------------------------
Plan hash value: 1838229974
---------------------------------------------------------------------------
COMMENTS
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 36 | 1476 | 412 (1)| 00:00:05 |
|* 1 | HASH JOIN | | 36 | 1476 | 412 (1)| 00:00:05 |
|* 2 | TABLE ACCESS FULL| T1 | 36 | 1080 | 67 (2)| 00:00:01 |
| 3 | TABLE ACCESS FULL| T2 | 87908 | 944K| 344 (1)| 00:00:05 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
COMMENTS
----------------------------------------------------------------------------------------------------
1 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
2 - filter("T1"."OBJECT_NAME" LIKE '%T1%' AND "T1"."OBJECT_NAME" IS
NOT NULL)
2- Using SQL Profile
--------------------
Plan hash value: 1022743391
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
COMMENTS
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 36 | 1476 | 139 (1)| 00:00:02 |
| 1 | NESTED LOOPS | | 36 | 1476 | 139 (1)| 00:00:02 |
| 2 | NESTED LOOPS | | 36 | 1476 | 139 (1)| 00:00:02 |
|* 3 | TABLE ACCESS FULL | T1 | 36 | 1080 | 67 (2)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | T2_IDX | 1 | | 1 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 11 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
COMMENTS
----------------------------------------------------------------------------------------------------
3 - filter("T1"."OBJECT_NAME" LIKE '%T1%' AND "T1"."OBJECT_NAME" IS NOT
NULL)
4 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
-------------------------------------------------------------------------------
SQL>
通过结果对比,显然加了HINT的方式效率更高。上面代码中的sql_id是从v$sql来,对应的是没有加hint的SQL。
结果看起来非常棒,SQL Tuning Advisor为我们找到了理想的执行计划,T1表上经过谓词过滤后返回的行数评估为36,相当地精确。我们要做的就是Accept SQL Profile,接受这个SQL Profile。这样一来,即使SQL正文不加hint该SQL也会按照我们的要求走。注意:执行计划的NOTE部分,有个SQL_PROFILE字样
SQL> execute dbms_sqltune.accept_sql_profile(task_name => :tuning_task,replace => TRUE,force_match=>true);
SQL> set autotrace traceonly
SQL> select t1.*,t2.owner from t1,t2 where t1.object_name like '%T1%' and t1.object_id=t2.object_id;
36 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1022743391
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 36 | 1476 | 139 (1)| 00:00:02 |
| 1 | NESTED LOOPS | | 36 | 1476 | 139 (1)| 00:00:02 |
| 2 | NESTED LOOPS | | 36 | 1476 | 139 (1)| 00:00:02 |
|* 3 | TABLE ACCESS FULL | T1 | 36 | 1080 | 67 (2)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | T2_IDX | 1 | | 1 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 11 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("T1"."OBJECT_NAME" LIKE '%T1%' AND "T1"."OBJECT_NAME" IS NOT
NULL)
4 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
Note
-----
- SQL profile "SYS_SQLPROF_0153ea63e5ff0000" used for this statement
Statistics
----------------------------------------------------------
34 recursive calls
0 db block gets
320 consistent gets
1 physical reads
144 redo size
2132 bytes sent via SQL*Net to client
541 bytes received via SQL*Net from client
4 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
36 rows processed
SQL>
我们再执行其他的类似SQL看看:比如修改like条件,再加几个空格修改大小写等
SQL> SElect t1.*,t2.owner from t1,t2 where t1.object_name like '%T2%' and t1.object_id=t2.object_ID;
57 rows selected.
Elapsed: 00:00:00.35
Execution Plan
----------------------------------------------------------
Plan hash value: 1022743391
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 36 | 1476 | 139 (1)| 00:00:02 |
| 1 | NESTED LOOPS | | 36 | 1476 | 139 (1)| 00:00:02 |
| 2 | NESTED LOOPS | | 36 | 1476 | 139 (1)| 00:00:02 |
|* 3 | TABLE ACCESS FULL | T1 | 36 | 1080 | 67 (2)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | T2_IDX | 1 | | 1 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 11 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("T1"."OBJECT_NAME" LIKE '%T2%' AND "T1"."OBJECT_NAME" IS NOT
NULL)
4 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
Note
-----
- SQL profile "SYS_SQLPROF_0153ea63e5ff0000" used for this statement
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
300 consistent gets
7 physical reads
0 redo size
2957 bytes sent via SQL*Net to client
552 bytes received via SQL*Net from client
5 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
57 rows processed
SQL>
卧槽,居然还是有效地!调用了同一个SQL Profile!即便cursor_sharing是EXACT的。因为force_match参数设为TRUE,那么dba_sql_profiles中的signature则是由SQL的force_matching_signature而来,否则便是exact_matching_signature。对于Outlines来说,则只能是exact_matching_signature。从这个角度上讲,Sql Profiles比Outlines的使用范围更广,因为Sql profiles对没有使用绑定变量的SQL也支持得很好。
SQL> show parameter cursor_sharing
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
cursor_sharing string EXACT
补充:对SQL语句,去掉重复的空格(不包括字符常量),将大小写转换成相同,比如均为大写(不包括字符常量)后,如果SQL相同,那么SQL语句的exact_matching_signature就是相同的。对SQL语句,去掉重复的空格(不包括字符常量),将大小写转换成相同,比如均为大写(不包括字符常量),然后去掉SQL中的常量,如果SQL相同,那么SQL语句的force_matching_signature就是相同的。但是例外的情况是:如果SQL中有绑定变量,force_matching_signature就会与exact_matching_signature一样的生成标准。
7、删除profile
如不再使用,可删除profile
SQL> exec dbms_sqltune.drop_sql_profile('SYS_SQLPROF_015431a0d02b0000');
相关文章推荐
- Oracle STA的使用
- Oracle数据库对象简介
- Oracle 12c单机数据库干净卸载
- 查看ORACLE 数据库及表信息
- Oracle Real Application Clusters 11g R2 新特性之SCAN -- 基础篇
- Oracle RAC root.sh 报错 Timed out waiting for the CRS stack to start 解决方法—--范例篇
- 数据库插入触发器 sql->oracle
- Oracle 11gR2 ASM备份/恢复metadata示例
- Oracle 11gR1/R2 PSU安装参考
- Linux 下Oracle 数据文件被物理误删除的恢复
- 常见Oracle错误之ORA-01102错误
- Oracle 12c Study之--Installer Oracle
- Oracle 12c Study之--Installer Oracle
- oracle sql jpa
- Oracle Logminer数据库日志分析详解
- oracle限制用户连接数
- Oracle行转列
- Oracle 和 SqlServer 的区别
- 几种不同oracle数据库启动方式的对比,及启动状态的切换、查询
- oracle数据库system密码忘记怎么办?