使用SQL_Profiles固定执行计划

使用 SQL Profiles主要解决2个棘手的问题:
在不让应用修改SQL的前提下,解决SQL的执行计划差的问题。
1、对于执行计划变差的SQL,根据历史的执行计划,固定好的执行计划,从而达到优化的目的( dbms_sqltune.accept_sql_profile
2、应用的SQL因为错加了hint导致走了差的执行计划, 在不能修改应用中的SQL的情况下,构造好的执行计划, 使SQL语句按指定的执行计划运行。( dbms_sqltune.import_sql_profile

该文章主要 介绍:
★什么是SQL Profiles及其作用
 如何使用SQL Tuning Advisor来生成SQL Profile,以及生成的SQL Profile产生的Hint。 同时也介绍了SQL的signature。

一、初步了解 SQL Profiles
试验过程:
1、 创建测试表及其索引
SQL> create table t1 as select object_id,object_name from dba_objects where rownum<=50000;
Table created.
SQL> create table t2 as select * from dba_objects;
Table created.
SQL> create index t2_idx on t2(object_id);
Index created.

2、查看当前的统计信息
SQL>select * from dba_tab_statistics where table_name=upper('&table_name');--新建的表当然没统计信息

3、对新建的表收集统计信息
SQL> exec dbms_stats.gather_table_stats('zhoushuai','t1',cascade=>true,method_opt=>'for all columns size 1');
PL/SQL procedure successfully completed.
SQL> exec dbms_stats.gather_table_stats('zhoushuai','t2',cascade=>true,method_opt=>'for all columns size 1');
PL/SQL procedure successfully completed.
--此时已经有统计信息了

4、执行一条SQL,查看并分析其执行计划
SQL>select t1.*,t2.owner from t1,t2 where t1.object_name like '%T1%' and t1.object_id=t2.object_id; 
...................---这里结果省略
SQL> set autotrace trace
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: 1838229974
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |  2500 |   100K|   418   (1)| 00:00:06 |
|*  1 |  HASH JOIN         |      |  2500 |   100K|   418   (1)| 00:00:06 |     《《《《《《《《《《《表链接为hash join
|*  2 |   TABLE ACCESS FULL| T1   |  2500 | 75000 |    70   (0)| 00:00:01 |      《《《《《《《《《《《两表均为全表扫
|   3 |   TABLE ACCESS FULL| T2   | 87274 |   937K|   348   (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
       1544  consistent gets                                 《《《《注意逻辑读
       1248  physical reads
          0  redo size
       1766  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
         10  sorts (memory)
          0  sorts (disk)
         36  rows processed
SQL> set autotrace off

PS:我们只注意关注返回的结果行数、执行计划以及逻辑读这些信息
分析:2个表都是全表扫,看过滤条件,T1有LIKE '%T1%',显然不能走索引正常,T2也是全表,所以这里有优化空间
      这里的问题在于执行计划ID=1的那一行,Oracle优化器评估T1 like ‘%T1%’返回的结果行数为2500行,即T1表总行数的5%,
 如果2个表采用index range scan+nested loop连接,oracle评估的成本会高于full table scan+hash join

5、修改SQL,分析Oracle优化器评估的index range_scan+nested loop的成本:
SQL> set autotrace trace
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;   2    3    4
36 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1022743391
---------------------------------------------------------------------------------------
| Id  | Operation                    | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |        |  2500 |   100K|  5071   (1)| 00:01:01 |
|   1 |  NESTED LOOPS                |        |  2500 |   100K|  5071   (1)| 00:01:01 |
|   2 |   NESTED LOOPS               |        |  2500 |   100K|  5071   (1)| 00:01:01 |
|*  3 |    TABLE ACCESS FULL         | T1     |  2500 | 75000 |    70   (0)| 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
        290  consistent gets
         14  physical reads
          0  redo size
       1777  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         36  rows processed
SQL>  set autotrace off

分析:可以看到Oracle优化器评估的成本为5071,远远高于原来的418。
         但是加了HINT之后实际的逻辑读只有290,低于原始SQL的1544。
所以这里可以看出来,由于Oracle优化器过高地估计了T1表经过like操作过滤返回的行数,
也就过高地估计了nest loop的成本,最终也就选择了不是最优的执行计划。

6、下面我们用Oracle的SQL Tuning Advisor来尝试优化这条SQL:
SQL> select sql_id,sql_text from v$sql where sql_text like 'select t1.*,t2.owner from t1,t2 where%';    ----找到SQL_ID
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

SQL> var tuning_task varchar2(100);  
SQL> DECLARE  
      l_sql_id v$session.prev_sql_id%TYPE;  
      l_tuning_task VARCHAR2(30);  
    BEGIN  
      l_sql_id:='4zbqykx89yc8v';  
      l_tuning_task := dbms_sqltune.create_tuning_task(sql_id => l_sql_id);  
      :tuning_task:=l_tuning_task;  
      dbms_sqltune.execute_tuning_task(l_tuning_task);  
      dbms_output.put_line(l_tuning_task);  
    END;  
    /  
PL/SQL procedure successfully completed.
SQL> print tuning_task;    
TUNING_TASK
------------------------
TASK_1833
SQL> SELECT dbms_sqltune.report_tuning_task(:tuning_task) FROM dual;  《《《《《《《《《《《查看建议


DBMS_SQLTUNE.REPORT_TUNING_TASK(:TUNING_TASK)
----------------------------------------------------------------------------------
GENERAL INFORMATION SECTION
-------------------------------------------------------------------------------
Tuning Task Name   : TASK_1833
Tuning Task Owner  : ZHOUSHUAI
Workload Type      : Single SQL Statement
Scope              : COMPREHENSIVE
Time Limit(seconds): 1800
Completion Status  : COMPLETED
Started at         : 04/05/2016 04:17:54
Completed at       : 04/05/2016 04:17:59
-------------------------------------------------------------------------------
Schema Name: ZHOUSHUAI
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 Finding (see explain plans section below)
--------------------------------------------------------
  A potentially better execution plan was found for this statement.
  Recommendation (estimated benefit: 79.99%)
  ------------------------------------------
  - Consider accepting the recommended SQL profile.
    execute dbms_sqltune.accept_sql_profile(task_name => 'TASK_1833',
            task_owner => 'ZHOUSHUAI', replace => TRUE);

  Validation results
  ------------------
  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):             .050657           .039519      21.98 %
  CPU Time (s):                 .049892           .038494      22.84 %
  User I/O Time (s):                  0                 0
  Buffer Gets:                     1494               296      80.18 %
  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.
  2. Statistics for the SQL profile plan were averaged over 10 executions.
-------------------------------------------------------------------------------
EXPLAIN PLANS SECTION
-------------------------------------------------------------------------------
1- Original With Adjusted Cost
------------------------------
Plan hash value: 1838229974
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |    36 |  1476 |   418   (1)| 00:00:06 |
|*  1 |  HASH JOIN         |      |    36 |  1476 |   418   (1)| 00:00:06 |
|*  2 |   TABLE ACCESS FULL| T1   |    36 |  1080 |    70   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| T2   | 87274 |   937K|   348   (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)

2- Using SQL Profile
--------------------
Plan hash value: 1022743391
---------------------------------------------------------------------------------------
| Id  | Operation                    | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |        |    36 |  1476 |   142   (0)| 00:00:02 |
|   1 |  NESTED LOOPS                |        |    36 |  1476 |   142   (0)| 00:00:02 |
|   2 |   NESTED LOOPS               |        |    36 |  1476 |   142   (0)| 00:00:02 |
|*  3 |    TABLE ACCESS FULL         | T1     |    36 |  1080 |    70   (0)| 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")
-------------------------------------------------------------------------------

分析:上面代码中的sql_id是从v$sql来,对应的是没有加hint的SQL。
结果看起来非常棒,SQL Tuning Advisor为我们找到了理想的执行计划,T1表上经过谓词过滤后返回的行数评估为36 相当地精确。
我们要做的就是Accept SQL Profile,接受这个SQL Profile。

7、使用这和profile
PS:这里重点强调,下面执行的这个比上边Recommendation的要多一个参数,force_match=>true,因为加上这个参数后,
功能类似于的cursor_sharing起到force效果,即使系统的是EXACT模式。
但是,注意,强烈建议该参数仅用于故障救急,稳定的生产环境最好不要加force_match=>true这个参数,否则会有意想不到的问题出现。
SQL> execute dbms_sqltune.accept_sql_profile(task_name => 'TASK_1833',task_owner => 'ZHOUSHUAI', replace => TRUE, force_match=>true);
PL/SQL procedure successfully completed.

SQL> set autotrace trace
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 |   142   (0)| 00:00:02 |
|   1 |  NESTED LOOPS                |        |    36 |  1476 |   142   (0)| 00:00:02 |
|   2 |   NESTED LOOPS               |        |    36 |  1476 |   142   (0)| 00:00:02 |
|*  3 |    TABLE ACCESS FULL         | T1     |    36 |  1080 |    70   (0)| 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_0153e2f3b7de0000" used for this statement  》》》》》》》这个名字是自动生成的
Statistics
----------------------------------------------------------
        122  recursive calls
          0  db block gets
        477  consistent gets
          3  physical reads
          0  redo size
       1766  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
         36  rows processed
分析:Note中果然使用了profile SYS_SQLPROF_0153e2f3b7de0000  

那我们在执行其他类似SQL:

SQL> select t1.*,t2.owner from t1,t2 where t1.object_name like '%T2%' and t1.object_id=t2.object_id;
57 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1022743391
---------------------------------------------------------------------------------------
| Id  | Operation                    | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |        |    57 |  2337 |   184   (0)| 00:00:03 |
|   1 |  NESTED LOOPS                |        |    57 |  2337 |   184   (0)| 00:00:03 |
|   2 |   NESTED LOOPS               |        |    57 |  2337 |   184   (0)| 00:00:03 |
|*  3 |    TABLE ACCESS FULL         | T1     |    57 |  1710 |    70   (0)| 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_0153e2f3b7de0000" used for this statement
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        301  consistent gets
          0  physical reads
          0  redo size
       2361  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         57  rows processed
分析:这里可以看到T2条件也使用的该profile,但是如果Recommendation不加force_match=>true的话,就不会参考使用该profile。

8、查看profile相关内容

   上面生成的profile名字为:SYS_SQLPROF_0153e2f3b7de0000
SQL> select name,category,signature,type,status,force_matching from dba_sql_profiles;
NAME                             CATEGORY      SIGNATURE   TYPE                  STATUS                   FORCE_MAT
-------------------------------------------------------- ------------------------------------------------------------
SYS_SQLPROF_0153e2f3b7de0000       DEFAULT     8.9755E+18 MANUAL                ENABLED           YES      《《《《FORCE_MAT 当不采用force_match=>true参数的时候,为NO

备注:查看相关profile内容可在sys.sqlobj$和sys.sqlobj$data视图中看到(11G)
总之, 一些复杂的SQL,我经常会先通过SQL Tuning Advisor来分析一下,看能不能让Oracle自已找出一个更好的执行计划。

我们来看看,SQL Profiles实际上是些什么:

SQL>col SIGNATURE for 999,999,999,999,999,999,999   
SQL> select a.signature,category,name,hint,sql_text
from (
  2    3  SELECT extractValue(value(h),'.') AS hint,
  4  od.signature signature
  5  FROM sys.sqlobj$data od, sys.sqlobj$ so,
  6  table(xmlsequence(extract(xmltype(od.comp_data),'/outline_data/hint'))) h
  7  WHERE so.signature = od.signature
  8  AND so.category = od.category
AND so.obj_type = od.obj_type
AND so.plan_id = od.plan_id
) a,
dba_sql_profiles b
where a.signature=b.signature;  9   10   11   12   13

                   SIGNATURE CATEGORY   NAME                                     HINT                                                             SQL_TEXT
---------------------------- ---------- ---------------------------------------- ---------------------------------------------------------------- ----------------------------------------------------------------------------------------------------
   8,975,541,025,552,400,288 DEFAULT    SYS_SQLPROF_0153e2f3b7de0000             OPT_ESTIMATE(@"SEL$1", TABLE, "T1"@"SEL$1", SCALE_ROWS=0.0144)     select t1.*,t2.owner from t1,t2 where t1.object_name like '%T1%' and t1.object_id=t2.object_id
   8,975,541,025,552,400,288 DEFAULT    SYS_SQLPROF_0153e2f3b7de0000             OPTIMIZER_FEATURES_ENABLE(default)                                 select t1.*,t2.owner from t1,t2 where t1.object_name like '%T1%' and t1.object_id=t2.object_id
 
 补充:10G可用下边SQL查看:
 --select name,category,signature,type,status,force_matching from dba_sql_profiles;  
 --select * from sys.sqlprof$attr;
 分析:signature可以理解为与sql_id、sql_hash_value类似的值,用来标识SQL。在10g以上的版本中,
 查看v$sql的定义就可以发现2列:exact_matching_signature、force_matching_signature。  
 
SQL>  select rownum,a.* from
  (select exact_matching_signature,force_matching_signature,plan_hash_value,sql_text
  from v$sql where sql_text like '%/*%xjs%' and sql_text not like '%v$sql%' order by 1) a;  2    3

    ROWNUM     EXACT_MATCHING_SIGNATURE     FORCE_MATCHING_SIGNATURE PLAN_HASH_VALUE SQL_TEXT
---------- ---------------------------- ---------------------------- --------------- ----------------------------------------------------------
         1    3,939,730,931,515,200,254   17,443,893,418,101,517,951      3617692013 select /* xjs */ object_name    from T1 where object_name='t1'
         2   10,964,210,455,693,560,558   11,097,449,316,038,436,385      3836375644 select /* xjs */ object_name    from T1 where rownum<=3
         3   10,964,210,455,693,560,558   11,097,449,316,038,436,385      3836375644 select /* xjs   */ object_name    from T1 where rownum<=3
         4   11,217,690,300,719,901,571      354,482,119,692,997,204      3836375644 select /* xjs */ 2 from t1 where rownum<=1
         5   11,974,975,582,747,367,847      354,482,119,692,997,204      3836375644 select /* xjs */ 1 from t1 where rownum<=1
         6   12,941,882,703,651,921,406   17,443,893,418,101,517,951      3617692013 select /* xjs */ object_name    from T1 where object_name='T1'
         7   17,986,178,357,953,662,359   11,097,449,316,038,436,385      3836375644 select /* xjs */ object_name    from T1 where rownum<=1
         8   17,986,178,357,953,662,359   11,097,449,316,038,436,385      3836375644 select /* xjs */ OBJECT_NAME from T1 where rownum<=1
         9   17,986,178,357,953,662,359   11,097,449,316,038,436,385      3836375644 SELECT /* xjs */ object_name    from T1 where rownum<=1
        10   17,986,178,357,953,662,359   11,097,449,316,038,436,385      3836375644 select /* xjs */ object_name from t1 where rownum<=1
10 rows selected.
分析:
第2、3条SQL的exact_matching_signature相同,第7、8、9、10条SQL的exact_matching_signature相同。
第2、3条SQL的force_matching_signature相同,第4、5条SQL的force_matching_signature相同,
第7、8、9、10条的SQL的force_matching_signature相同。第1、6条SQL的force_matching_signature相同

结论:
对SQL语句,去掉重复的空格(不包括字符常量),将大小写转换成相同,比如均为大写(不包括字符常量)后,如果SQL相同,那么SQL语句的exact_matching_signature就是相同的。
对SQL语句,去掉重复的空格(不包括字符常量),将大小写转换成相同,比如均为大写(不包括字符常量),然后去掉SQL中的常量,如果SQL相同,那么SQL语句的force_matching_signature就是相同的。
但是例外的情况是:如果SQL中有绑定变量,force_matching_signature就会与exact_matching_signature一样的生成标准。
 SQL> select rownum,a.* from
(select exact_matching_signature,force_matching_signature,plan_hash_value,sql_text
from v$sql where sql_text like '%/*%xjs2%' and sql_text not like '%v$sql%' order by 1) a;   2    3
    ROWNUM     EXACT_MATCHING_SIGNATURE     FORCE_MATCHING_SIGNATURE PLAN_HASH_VALUE SQL_TEXT
---------- ---------------------------- ---------------------------- --------------- ----------------------------------------------------------------
         1    5,363,536,431,331,905,229    5,363,536,431,331,905,229      3836375644  select /* xjs2 */ object_name    from T1 where object_name='T1' and rownum<=:rn
         2    5,363,536,431,331,905,229    5,363,536,431,331,905,229      3836375644  select /* xjs2 */ object_name    from t1 where object_name='T1' and rownum<=:rn
         3   12,992,689,086,515,482,106   12,992,689,086,515,482,106      3836375644  select /* xjs2 */ object_name    from t1 where object_name='T2' and rownum<=:rn
 
 可以看到,现在exact_matching_signature与force_matching_signature完全一样了。
从force_matching_signature的特性,我们可以想到一个用途
用于查找没有使用绑定变量的SQL语句,类似于使用plan_hash_value来查找。

回到前面,accept_sql_profile这个过程,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 profiles的force_match属性是不能更改的,只能在创建时指定,如果要更改,则只能重新创建改Sql Profile。
下面来看看sys.sqlprof$attr数据字典。这里面没有SQL Profile的名字,而是用的sql的signature。
 OPT_ESTIMATE(@"SEL$1", TABLE, "T1"@"SEL$1", SCALE_ROWS=0.0144)
 可以看到,SQL Profiles的attr_val实际上就是一些Hints,这跟Outlines没有本质上的区别。只是SQL Profiles中的Hint,没有指定SQL使用哪个索引,也没有指定表的连接方法和连接顺序。
 这里只指定了T1表评估返回的行数,与原始的评估返回的行数的放大缩小的倍数。2500*0.0144刚好等于36。这里就是告诉Oracle优化器,
 T1表经过谓语过滤后返回行数应该为评估的0.0144倍。
从这里可以看出,SQL Profiles并不会锁定SQL的执行计划,只是提供了更多、更准确的统计信息给优化器。

 看下面的测试:
SQL>  exec dbms_stats.set_table_stats('ZHOUSHUAI','T1',numrows=>5000000);
PL/SQL procedure successfully completed.
SQL> @showplan
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 1838229974
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |  3600 |   144K|   455   (9)| 00:00:06 |
|*  1 |  HASH JOIN         |      |  3600 |   144K|   455   (9)| 00:00:06 |
|*  2 |   TABLE ACCESS FULL| T1   |  3600 |   105K|   107  (35)| 00:00:02 |
|   3 |   TABLE ACCESS FULL| T2   | 87274 |   937K|   348   (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)
Note
-----
   - SQL profile "SYS_SQLPROF_0153e2f3b7de0000" used for this statement
21 rows selected.
将T1表的统计信息中的表行数改为500万,Oracle就会评估为返回5000000*5%*0.0144=3600行。这里执行计划又变回为full scan+hash join。
可以看到,虽然SQL Profile起作用了,但是并没有锁定执行计划。



二、如何手工来为创建、生成SQL Profile,以及如何让SQL Profile也能像Outlines一样锁定SQL的执行计划,以保持SQL执行计划的稳定性。 
 
1、
如何来手工创建SQL Profiles?
 SQL> desc dbms_sqltune  
 PROCEDURE IMPORT_SQL_PROFILE
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 SQL_TEXT                       CLOB                    IN
 PROFILE                        SQLPROF_ATTR            IN
 NAME                           VARCHAR2                IN     DEFAULT
 DESCRIPTION                    VARCHAR2                IN     DEFAULT
 CATEGORY                       VARCHAR2                IN     DEFAULT
 VALIDATE                       BOOLEAN                 IN     DEFAULT
 REPLACE                        BOOLEAN                 IN     DEFAULT
 FORCE_MATCH                    BOOLEAN                 IN     DEFAULT
 这个过程其并不仅仅IMPORT,根据REPLACE的选项可以实现 CREATE OR REPLACE SQL_PROFILE的功能。
 过程中的PROFILE参数为SYS.SQLPROF_ATTR,这种类型其实就是VARCHAR2的集合类型(COLLECTION):
如下

 SQL> select text from dba_source where name='SQLPROF_ATTR' and owner='SYS'; 
TEXT
----------------------------------------------------------------------------------------------------
TYPE     sqlprof_attr
AS VARRAY(2000) of VARCHAR2(500)

2、下面我们就用这个过程来创建SQL PROFILE:
首先为避免干扰,前边测试中生成的SQL Profile删除掉,同时恢复T1表的统计信息中的表行数:
SQL> exec dbms_sqltune.drop_sql_profile('SYS_SQLPROF_0153e2f3b7de0000'); 
PL/SQL procedure successfully completed.
SQL> exec dbms_stats.set_table_stats('ZHOUSHUAI','T1',numrows=>49953);  
PL/SQL procedure successfully completed.
完成后查看该表的信息:
SQL> @tabstat.sql   
Please enter Name of Table Owner: zhoushuai
Please enter Table Name : t1
**********************************************************
Table Level
**********************************************************
Table                                  Number                        Empty    Chain Average Global         Sample Date
Name                                  of Rows          Blocks       Blocks    Count Row Len Stats            Size MM-DD-YYYY
------------------------------ -------------- --------------- ------------ -------- ------- ------ -------------- ----------
T1                                     49,953            2,52            0        0      30 YES            50,000 04-13-2016
Column                             Distinct              Number       Number         Sample Date
Name                                 Values     Density Buckets        Nulls           Size MM-DD-YYYY
------------------------------ ------------ ----------- ------- ------------ -------------- ----------
OBJECT_ID                            50,000   .00002000       1            0         50,000 04-05-2016
OBJECT_NAME                          28,836   .00003468       1            0         50,000 04-05-2016

**********************************************************
Partition Level
**********************************************************

现在手工创建一个SQL Profile:
SQL> declare  
  2      v_hints sys.sqlprof_attr;  
  3    begin  
  4      v_hints:=sys.sqlprof_attr('USE_NL(T1 T2)','INDEX(T2)');  
  5      dbms_sqltune.import_sql_profile('select t1.*,t2.owner from t1,t2 where t1.object_name like ''%T1%'' and t1.object_id=t2.object_id',  
  6                  v_hints,'SQLPROFILE_NAME1',force_match=>true);  
  7    end;
  8  /

PL/SQL procedure successfully completed.
SQL>  select a.signature,category,name,hint,sql_text
  2  from (
  3         SELECT extractValue(value(h),'.') AS hint,
  4     od.signature signature
  5     FROM sys.sqlobj$data od, sys.sqlobj$ so,
  6     table(xmlsequence(extract(xmltype(od.comp_data),'/outline_data/hint'))) h
  7     WHERE so.signature = od.signature
  8     AND so.category = od.category
  9  AND so.obj_type = od.obj_type
 10  AND so.plan_id = od.plan_id
 11  ) a,
 12  dba_sql_profiles b
 13  where a.signature=b.signature;


             SIGNATURE CATEGORY                NAME                          HINT              SQL_TEXT
---------------------- ------------ -----------------  ----------------------------------------------------------------------------------------------------------------------
   3960696072677096522 DEFAULT      SQLPROFILE_NAME1   USE_NL(T1 T2)     select t1.*,t2.owner from t1,t2 where t1.object_name like '%T1%' and t1.object_id=t2.object_id
   3960696072677096522 DEFAULT      SQLPROFILE_NAME1   INDEX(T2)         select t1.*,t2.owner from t1,t2 where t1.object_name like '%T1%' and t1.object_id=t2.object_id
分析:可以看到,该SQL已经配置了我们想要的hint的profile

3、查看执行计划

SQL> set autotrace trace
SQL> select  t1.*,t2.owner  
  2           from t1,t2  
  3           where t1.object_name like '%T1%'  
  4           and t1.object_id=t2.object_id;
36 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1838229974
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |  2498 | 97422 |   418   (1)| 00:00:06 |
|*  1 |  HASH JOIN         |      |  2498 | 97422 |   418   (1)| 00:00:06 |
|*  2 |   TABLE ACCESS FULL| T1   |  2498 | 74940 |    70   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| T2   | 87274 |   767K|   348   (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)
Note
-----
   - SQL profile "SQLPROFILE_NAME1" used for this statement
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       1493  consistent gets
       1247  physical reads
          0  redo size
       1766  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         36  rows processed
可以看到,SQL使用了SQL Profile  SQLPROFILE_NAME1,不过没有达到我们预期的效果
分析: 看起来是SQL Profile使用的Hints有问题

4.我们重新设置SQL Profile的Hints,在Hints中加上“Query Block Name”

这一次在执行IMPORT_SQL_PROFILE过程时,将REPLACE参数设置为TRUE,以替换现有的SQL Profile
SQL> declare
  2     v_hints sys.sqlprof_attr;  
  3      begin  
  4        v_hints:=sys.sqlprof_attr('USE_NL(T1@SEL$1 T2@SEL$1)','INDEX(T2@SEL$1)');  
  5        dbms_sqltune.import_sql_profile('select t1.*,t2.owner from t1,t2 where t1.object_name like ''%T1%'' and t1.object_id=t2.object_id',  
  6                    v_hints,'SQLPROFILE_NAME1',force_match=>true,replace=>true);  
  7      end;  
  8      /  
查看profile:
PL/SQL procedure successfully completed.
   SQL>  select a.signature,category,name,hint,sql_text
  2  from (
  3         SELECT extractValue(value(h),'.') AS hint,
  4     od.signature signature
  5     FROM sys.sqlobj$data od, sys.sqlobj$ so,
  6     table(xmlsequence(extract(xmltype(od.comp_data),'/outline_data/hint'))) h
  7     WHERE so.signature = od.signature
  8     AND so.category = od.category
  9  AND so.obj_type = od.obj_type
 10  AND so.plan_id = od.plan_id
 11  ) a,
 12  dba_sql_profiles b
 13  where a.signature=b.signature;


             SIGNATURE CATEGORY   NAME              HINT                      SQL_TEXT
---------------------- ---------- ----------------- ------------------------------------------------------------------------------------------------------------------------------
   3960696072677096522 DEFAULT    SQLPROFILE_NAME1  USE_NL(T1@SEL$1 T2@SEL$1) select t1.*,t2.owner from t1,t2 where t1.object_name like '%T1%' and t1.object_id=t2.object_id
   3960696072677096522 DEFAULT    SQLPROFILE_NAME1  INDEX(T2@SEL$1)           select t1.*,t2.owner from t1,t2 where t1.object_name like '%T1%' and t1.object_id=t2.object_id

6、执行该sql查看执行计划:
SQL> set autotrace trace
SQL> select  t1.*,t2.owner  
  2           from t1,t2  
  3           where t1.object_name like '%T1%'  
  4           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             |        |  2498 | 97422 |  5067   (1)| 00:01:01 |
|   1 |  NESTED LOOPS                |        |  2498 | 97422 |  5067   (1)| 00:01:01 |
|   2 |   NESTED LOOPS               |        |  2498 | 97422 |  5067   (1)| 00:01:01 |
|*  3 |    TABLE ACCESS FULL         | T1     |  2498 | 74940 |    70   (0)| 00:00:01 |
|*  4 |    INDEX RANGE SCAN          | T2_IDX |     1 |       |     1   (0)| 00:00:01 |
|   5 |   TABLE ACCESS BY INDEX ROWID| T2     |     1 |     9 |     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 "SQLPROFILE_NAME1" used for this statement
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        298  consistent gets
          0  physical reads
          0  redo size
       1766  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         36  rows processed
分析:这一次达到了预期的效果。看起来在SQL Profiles中对Hints还有一定的要求。

7、修改统计信息测试:
那么我们再一次手工修改T1表的统计信息,看看结果如何:

SQL> exec dbms_stats.set_table_stats('ZHOUSHUAI','T1',numrows=>5000000);  
PL/SQL procedure successfully completed.
SQL> select  t1.*,t2.owner  
  2           from t1,t2  
  3           where t1.object_name like '%T1%'  
  4           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             |        |   250K|  9521K|   500K  (1)| 01:40:03 |
|   1 |  NESTED LOOPS                |        |   250K|  9521K|   500K  (1)| 01:40:03 |
|   2 |   NESTED LOOPS               |        |   250K|  9521K|   500K  (1)| 01:40:03 |
|*  3 |    TABLE ACCESS FULL         | T1     |   250K|  7324K|   107  (35)| 00:00:02 |
|*  4 |    INDEX RANGE SCAN          | T2_IDX |     1 |       |     1   (0)| 00:00:01 |
|   5 |   TABLE ACCESS BY INDEX ROWID| T2     |     1 |     9 |     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 "SQLPROFILE_NAME1" used for this statement
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        298  consistent gets
          0  physical reads
          0  redo size
       1766  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         36  rows processed
分析:可以看到,Oracle优化器评估表T1经过Like条件过滤后返回的行数虽然很大,
但是这里的执行计划仍然与未修改统计信息之前一样,使用range scan+ nested loop join。
通过以上的测试,我们明白了DBMS_SQLTUNE.IMPORT_SQL_PROFILE的使用,同时也验证了这种方式的有效性,
SQL Profiles能够像Outlines一样,能够稳定SQL的执行计划。

三、接下来我们需要完成两个任务

任务一:对现有的SQL稳定其执行计划(针对执行计划不稳定的SQL)

这里的问题是:
稳定一条SQL语句的Hints从哪里来?
简单的sql,没问题,我们可以手工构造,但是复杂的SQL,手工构造相对比较复杂,同时手工构造的Hints不一定能够保证SQL的执行计划就会稳定。
从10g开始,v$sql_plan中就包括了SQL语句OUTLINE数据,也就是稳定执行计划的Hints。
从下面可以看到:

SQL> select  t1.*,t2.owner  
  2           from t1,t2  
  3           where t1.object_name like '%T1%'  
  4           and t1.object_id=t2.object_id;  

36 rows selected.
执行完上边SQL后执行下边
SQL> select * from table(dbms_xplan.display_cursor(null,null,'outline')); 


PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------------------
SQL_ID  77bdyugrbkj0y, child number 1
-------------------------------------
select  t1.*,t2.owner          from t1,t2          where t1.object_name
like '%T1%'          and t1.object_id=t2.object_id

Plan hash value: 1022743391
---------------------------------------------------------------------------------------
| Id  | Operation                    | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |        |       |       |   500K(100)|          |
|   1 |  NESTED LOOPS                |        |   250K|  9521K|   500K  (1)| 01:40:03 |
|   2 |   NESTED LOOPS               |        |   250K|  9521K|   500K  (1)| 01:40:03 |
|*  3 |    TABLE ACCESS FULL         | T1     |   250K|  7324K|   107  (35)| 00:00:02 |
|*  4 |    INDEX RANGE SCAN          | T2_IDX |     1 |       |     1   (0)| 00:00:01 |
|   5 |   TABLE ACCESS BY INDEX ROWID| T2     |     1 |     9 |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Outline Data
-------------
  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('11.2.0.4')
      DB_VERSION('11.2.0.4')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$1")
      FULL(@"SEL$1" "T1"@"SEL$1")
      INDEX(@"SEL$1" "T2"@"SEL$1" ("T2"."OBJECT_ID"))
      LEADING(@"SEL$1" "T1"@"SEL$1" "T2"@"SEL$1")
      USE_NL(@"SEL$1" "T2"@"SEL$1")
      NLJ_BATCHING(@"SEL$1" "T2"@"SEL$1")
      END_OUTLINE_DATA
  */
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 SQLPROFILE_NAME1 used for this statement
47 rows selected.
分析:上面所显示的“Outline Data”即是我们稳定SQL执行计划需要的Hints(我们甚至可以将这些Hints直接写到我们的SQL中)。
对需要稳定执行计划的SQL,我们所要做的就是如前面所示,将Hints与SQL文本一起创建一个SQL Profile。
这里不得不提到一个SQL脚本,来自MOS。
”SQLT (SQLTXPLAIN) - Tool that helps to diagnose SQL statements performing poorly [ID 215187.1]“,
在这篇文章中点击 [10.2, 11.1, 11.2, 12.1 download] 可以下载到sqlt.zip,这个压缩文件内有一个文件:coe_xfr_sql_profile.sql。
这个脚本可以用于从shared pool、awr中提取指定的SQL ID的Outline Data并创建SQL Profile。

下面是示例:

QL>  select s.SQL_ID,s.PLAN_HASH_VALUE,s.SQL_FULLTEXT  from v$sql s where sql_text like 'select  t1.*,t2.owner%'; 
SQL_ID                                  PLAN_HASH_VALUE
--------------------------------------- ---------------
SQL_FULLTEXT
---------------------------------------------------------------
77bdyugrbkj0y                                1022743391   ---这是要找的SQL_ID和PLAN_HASH_VALUE
                                                          ---注意的是生产环境要选择你认为最优的那个作为下边脚本的输入值
select  t1.*,t2.owner
         from t1,t2
         where t1.object_name like '%T1%'
         and t1.object_id=t2.object_id
 
或用下边这个命令查更准确确定哪个hash的执行计划最好,因为执行计划不稳定的会有多个PLAN_HASH_VALUE
/*
select distinct SQL_ID,PLAN_HASH_VALUE,to_char(TIMESTAMP,'yyyymmdd hh24:mi:ss')  TIMESTAMP
from dba_hist_sql_plan 
where SQL_ID='77bdyugrbkj0y' order by TIMESTAMP;
col options for a15
col operation for a20
col object_name for a20
select plan_hash_value,id,operation,options,object_name,depth,cost,to_char(TIMESTAMP,'yyyymmdd hh24:mi:ss')
    from DBA_HIST_SQL_PLAN  
    where sql_id ='77bdyugrbkj0y' 
    and plan_hash_value in (1022743391)
    order by ID,TIMESTAMP;
*/  
 
找到对应的SQL_ID和PLAN_HASH_VALUE
SQL>  @coe_xfr_sql_profile.sql 77bdyugrbkj0y 1022743391
Parameter 1:
SQL_ID (required)
Parameter 2:
PLAN_HASH_VALUE (required)
Values passed to coe_xfr_sql_profile:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SQL_ID         : "77bdyugrbkj0y"
PLAN_HASH_VALUE: "1022743391"
........................中间部分省略(会提示执行下边的内容)
SQL>@coe_xfr_sql_profile_77bdyugrbkj0y_1022743391.sql

执行完成后,查看profile大概内容为(可以用上边查看profile的脚本查看):
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('11.2.0.4')
DB_VERSION('11.2.0.4')
ALL_ROWS
OUTLINE_LEAF(@"SEL$1")
FULL(@"SEL$1" "T1"@"SEL$1")
INDEX(@"SEL$1" "T2"@"SEL$1" ("T2"."OBJECT_ID"))
LEADING(@"SEL$1" "T1"@"SEL$1" "T2"@"SEL$1")
USE_NL(@"SEL$1" "T2"@"SEL$1")
NLJ_BATCHING(@"SEL$1" "T2"@"SEL$1")
END_OUTLINE_DATA

c oe_xfr_sql_profile.sql这个脚本首先要求输入sql_id,然后从shared pool、awr中获取sql执行的各个执行计划的统计信息
执行计划不稳定的SQL通常会有多个不同的执行计划),然后输入你认为是正确的、需要稳定的执行计划的hash value,
脚本就会生成另一个脚本,这里为coe_xfr_sql_profile_77bdyugrbkj0y_1022743391.sql,
然后运行这个脚本,就会创建出稳定执行计划所需要的SQL Profile,
SQL Profile的名字为:coe+sql_id+plan_hash_value,这里为coe_77bdyugrbkj0y_1022743391。
注意,这里创建的SQL Profile,force match默认为FALSE,我们可以手工修改脚本将其改为TRUE,
同时我们也可以按意愿来修改生成的脚本的其他内容。




任务二:不改sql,即使SQL已经使用hints。固定执行计划


常常遇到这样的情况,SQL语句其执行计划有问题,或者是SQL使用了错误的Hints(比如 /*+ RULE */)导致SQL性能较差,
但是应用又不能修改或者时间内不能修改,那么我们怎么来改变SQL的执行计划呢。
有3种办法,
一种是调整统计信息,这个不建议使用,因为比较复杂、不稳定可靠(统计信息可能会重新收集),影响面广(会影响其他访问此对象的SQL)。
二种是使用OUTLINE,这种方法比较复杂。
三种就是我们今天要介绍的使用SQL Profiles了。
使用SQL Profiles来改变SQL的执行计划,其本质上就是使用Hints来改变SQL的执行计划。


1、简单的SQL,我们同样可以像前面一样手工构造Hints然后再使用DBMS_SQLTUNE.IMPORT_SQL_PROFILE来实现。(但是这种方法还是略显烦琐)
2、取得原始SQL的文本(如有可能还包括sql id)
构造一个与原始SQL在逻辑上、结构上完全相同的SQL。
这里强制逻辑上和结构上相同,SQL解析的用户名、SQL中引用对象的用户名甚至是一些predicate条件都可以不同。(当然能够与原始SQL完全一样就更省事。)
执行我们构造的SQL,并取得构造的SQL的Outline Data。
使用原始SQL的文本和构造的SQL的Outline Data创建SQL Profile。

下面我们来演示一下整个过程。我们这里要修改执行计划的SQL是:
select /*+ orig_sql full(t1) full(t2) use_hash(t1 t2) */ t1.*,t2.owner     
     from t1,t2     
     where t1.object_name like '%T1%'     
     and t1.object_id=t2.object_id; 

SQL> select s.SQL_ID,s.PLAN_HASH_VALUE,s.SQL_FULLTEXT  from v$sql s where sql_text like 'select /*+ orig_sql%'; 
SQL_ID                                  PLAN_HASH_VALUE
--------------------------------------- ---------------
SQL_FULLTEXT
----------------------------------------------------------------------
gmvb9bp7f9kqd                                1838229974
select /*+ orig_sql full(t1) full(t2) use_hash(t1 t2) */ t1.*,t2.owner
     from t1,t2
     where t1.object_name like '%T1%'
     and t1.object_id=t2.object_id
SQL> select /*+ orig_sql full(t1) full(t2) use_hash(t1 t2) */ t1.*,t2.owner     
  2       from t1,t2     
  3       where t1.object_name like '%T1%'     
  4       and t1.object_id=t2.object_id;
36 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1838229974

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |  2498 | 97422 |   418   (1)| 00:00:06 |
|*  1 |  HASH JOIN         |      |  2498 | 97422 |   418   (1)| 00:00:06 |
|*  2 |   TABLE ACCESS FULL| T1   |  2498 | 74940 |    70   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| T2   | 87274 |   767K|   348   (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
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       1493  consistent gets
       1247  physical reads
          0  redo size
       1766  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         36  rows processed

SQL> select /*+ modify_sql index(t1) use_nl(t1 t2) */ t1.*,t2.owner  
  2           from t1,t2  
  3           where t1.object_name like '%T1%'  
  4           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             |        |  2498 | 97422 |  5067   (1)| 00:01:01 |
|   1 |  NESTED LOOPS                |        |  2498 | 97422 |  5067   (1)| 00:01:01 |
|   2 |   NESTED LOOPS               |        |  2498 | 97422 |  5067   (1)| 00:01:01 |
|*  3 |    TABLE ACCESS FULL         | T1     |  2498 | 74940 |    70   (0)| 00:00:01 |
|*  4 |    INDEX RANGE SCAN          | T2_IDX |     1 |       |     1   (0)| 00:00:01 |
|   5 |   TABLE ACCESS BY INDEX ROWID| T2     |     1 |     9 |     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
----------------------------------------------------------
         36  recursive calls
          0  db block gets
        336  consistent gets
          0  physical reads
          0  redo size
       1766  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          9  sorts (memory)
          0  sorts (disk)
         36  rows processed
SQL> select s.SQL_ID,s.PLAN_HASH_VALUE,s.SQL_FULLTEXT  from v$sql s where sql_text like 'select /*+ modify_sql index(t1)%'; 
SQL_ID                                  PLAN_HASH_VALUE
--------------------------------------- ---------------
SQL_FULLTEXT
--------------------------------------------------------
0vdkn8rysa3j1                                1022743391
select /*+ modify_sql index(t1) use_nl(t1 t2) */ t1.*,t2.owner
         from t1,t2
         where t1.object_name like '%T1%'
         and t1.object_id=t2.object_id

1 row selected.
 
然后使用coe_xfr_sql_profile.sql脚本来提取我们 构造 的SQL的Outline Data,
生成的结果为coe_xfr_sql_profile_0vdkn8rysa3j1_1022743391.sql,打开结果文件,
可以看到有这么一段  
h := SYS.SQLPROF_ATTR(
q'[BEGIN_OUTLINE_DATA]',
q'[IGNORE_OPTIM_EMBEDDED_HINTS]',
q'[OPTIMIZER_FEATURES_ENABLE('11.2.0.4')]',
q'[DB_VERSION('11.2.0.4')]',
q'[ALL_ROWS]',
q'[OUTLINE_LEAF(@"SEL$1")]',
q'[FULL(@"SEL$1" "T1"@"SEL$1")]',
q'[INDEX(@"SEL$1" "T2"@"SEL$1" ("T2"."OBJECT_ID"))]',
q'[LEADING(@"SEL$1" "T1"@"SEL$1" "T2"@"SEL$1")]',
q'[USE_NL(@"SEL$1" "T2"@"SEL$1")]',
q'[NLJ_BATCHING(@"SEL$1" "T2"@"SEL$1")]',
q'[END_OUTLINE_DATA]');

再针对gmvb9bp7f9kqd使用coe_xfr_sql_profile.sql,生成的结果文件为coe_xfr_sql_profile_gmvb9bp7f9kqd_1838229974.sql。手工修改这个文件,将里面h := SYS.SQLPROF_ATTR…那一段替换成我们之前得到的那一段。这一次我们将这个文件中的force_match从FALSE改成TRUE。
最后我们运行coe_xfr_sql_profile_gmvb9bp7f9kqd_1838229974.sql这个脚本文件:
SQL> WHENEVER SQLERROR CONTINUE
SQL> SET ECHO OFF;
            SIGNATURE
---------------------
 15409905709853673912
           SIGNATUREF
---------------------
  9964126883768520950
... manual custom SQL Profile has been created
COE_XFR_SQL_PROFILE_gmvb9bp7f9kqd_1838229974 completed  
样就完成了我们所需要的SQL Profile的创建。
下面再看看原来的SQL执行情况(这里将like条件改了一下,以查看force match是否起作用):
SQL> set autotrace trace
SQL> select /*+ orig_sql full(t1) full(t2) use_hash(t1 t2) */ t1.*,t2.owner
  2       from t1,t2
  3       where t1.object_name like '%T2%'
  4       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             |        |  2498 | 97422 |  5067   (1)| 00:01:01 |
|   1 |  NESTED LOOPS                |        |  2498 | 97422 |  5067   (1)| 00:01:01 |
|   2 |   NESTED LOOPS               |        |  2498 | 97422 |  5067   (1)| 00:01:01 |
|*  3 |    TABLE ACCESS FULL         | T1     |  2498 | 74940 |    70   (0)| 00:00:01 |
|*  4 |    INDEX RANGE SCAN          | T2_IDX |     1 |       |     1   (0)| 00:00:01 |
|   5 |   TABLE ACCESS BY INDEX ROWID| T2     |     1 |     9 |     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 "coe_gmvb9bp7f9kqd_1838229974" used for this statement
Statistics
----------------------------------------------------------
        420  recursive calls
          0  db block gets
        713  consistent gets
          1  physical reads
          0  redo size
       1766  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
         51  sorts (memory)
          0  sorts (disk)
         36  rows processed
可以看到SQL Profile起作用了。
对于SQL Profiles来说,不属于任何一个用户,比Outlines更具有操控性灵活性。
 
--补充知识
 
DBMS_SQLTUNE.DROP_SQL_PROFILE( name =>'my_sql_profile' );
通过select * fromdba_sql_profiles a;查询出PROFILE 的NAME,
然后执行下面的可以删除对应的sql_profile
execdbms_sqltune.drop_sql_profile('coe_br1mv51g21cxw_3677037978');  

该文章实验参考:http://blog.itpub.net/25380220/viewspace-697573/
                         http://blo g.itpub.net/25380220/viewspace-697583/ 



来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/31043804/viewspace-2081512/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/31043804/viewspace-2081512/

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值