oracle固定执行计划2-sql profile


上篇介绍了如何使用SQL Tuning Advisor来生成SQL Profile,以及生成的SQL Profile产生的Hint。本篇将介绍如何手工创建SQL Profiles(即不通过SQL Tuning Advisor)。那么,这里最关键的一点是,如何来手工创建SQL Profiles?
使用DBMS_SQLTUNE.IMPORT_SQL_PROFILE过程。

mauanl类型的sql profile

绑定已经存在的执行计划

其实就是加hint,为了方便,其实就是拿脚本绑定,有3个脚本:
1、极简版
2、coe_xfr_sql_profile
3、coe_load_sql_profile
coe_xfr_sql_profile.sql脚本来固定sql 执行计划,但是这个脚本操作起来比较麻烦,而且容易出错。这个脚本的正确用途是用来做不同数据库之间sql执行计划的固定。
最方便的脚本是:coe_load_sql_profile.sql,使用这个脚本,只需要输入几个参数,就能完成快速恢复执行计划的任务。
但是也分为2中情况,一是1个sql,有多个执行计划,即sql_id相同,但是plan hash value不同,拿好的plan hash value去绑定坏的plan hash value;
二是,为了生成最优的执行计划,人为给原sql加了hint,生成新的sql,生成新的执行计划,拿好的sql_id和plan hash value去绑定坏的sql_id
这3个脚本的使用前提都是有更优的执行计划存在内存中

  1. hint必须加正确,手动加hint绑定不容易
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
PROCEDURE IMPORT_SQL_PROFILE
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 SQL_TEXT                       CLOB                    IN
 PROFILE_XML                    CLOB                    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
 。。。

这个过程其名字与实际功能有所差异,其实可以理解为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)

为避免干扰,将上一篇测试中生成的SQL Profile删除掉,同时恢复T1表的统计信息中的表行数:

exec dbms_stats.set_table_stats('SCOTT','T1',numrows=>5000000);

session 1:
SQL> exec dbms_sqltune.drop_sql_profile('SYS_SQLPROF_0177dc7534bb0000'); 

PL/SQL procedure successfully completed.

SQL> exec dbms_sqltune.drop_sql_profile('SYS_SQLPROF_0177dc7ca26c0001'); 

PL/SQL procedure successfully completed.

现在我们手工创建一个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 COMP_DATA from dba_sql_profiles a, SQLOBJ$DATA b  
  2  where a.signature = b.signature  
  3  and a.name='SQLPROFILE_NAME1';  

COMP_DATA
----------------------------------------------------------------------------------------------------
<outline_data><hint><![CDATA[USE_NL(T1 T2)]]></hint><hint><![CDATA[INDEX(T2)]]></hint></outline_data>

下面执行SQL Profiles对应的SQL:

session 2:
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|   391   (1)| 00:00:05 |
|*  1 |  HASH JOIN         |      |  2500 |   100K|   391   (1)| 00:00:05 |
|*  2 |   TABLE ACCESS FULL| T1   |  2500 | 75000 |    70   (0)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| T2   | 80073 |   860K|   321   (1)| 00:00:04 |
---------------------------------------------------------------------------

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
----------------------------------------------------------
         45  recursive calls
          0  db block gets
       1444  consistent gets
          1  physical reads
          0  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
         11  sorts (memory)
          0  sorts (disk)
         36  rows processed

–虽然用了sqlprofile,但是没生效。
虽然执行计划的输出Note部分显示已经使用到了SQL Profile,但是执行计划并没有如我们预期一样被改变,依然是全表扫描,查看存储hint的基表也显示索引扫描的hint已经被绑定到了这个SQL上,那么问题出哪了?
这是由于SQL Profile对于hint是非常挑剔的,SQL Profile里接受的hint需要提供Query Block Name(初始化参数类的hint不需要提供Query Block Name),否则优化器会忽略掉这些hint,我们重新设置SQL Profile的Hints,在Hints中加上Query Block Name看看。
看起来是SQL Profile使用的Hints有问题。我们重新设置SQL Profile的Hints,在Hints中加上“Query Block Name"。这一次在执行IMPORT_SQL_PROFILE过程时,将REPLACE参数设置为TRUE,以替换现有的SQL Profile:

session 1:
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  / 

PL/SQL procedure successfully completed.

SQL> select COMP_DATA from dba_sql_profiles a, SQLOBJ$DATA b  
  2  where a.signature = b.signature  
  3  and a.name='SQLPROFILE_NAME1';  

COMP_DATA
----------------------------------------------------------------------------------------------------
<outline_data><hint><![CDATA[USE_NL(T1@SEL$1 T2@SEL$1)]]></hint><hint><![CDATA[INDEX(T2@SEL$1)]]></h
int></outline_data>

再次执行下面的SQL:

session 2:
SQL> /

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")

Note
-----
   - SQL profile "SQLPROFILE_NAME1" used for this statement


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
        306  consistent gets
          0  physical reads
          0  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
          0  sorts (memory)
          0  sorts (disk)
         36  rows processed

已生效。这一次达到了预期的效果。看起来在SQL Profiles中对Hints还有一定的要求。
那么我们再一次手工修改T1表的统计信息,看看结果如何:
session 1:
修改统计信息:

SQL>  exec dbms_stats.set_table_stats('SCOTT','T1',numrows=>5000000);

PL/SQL procedure successfully completed.
session 2:
SQL> /

36 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 1022743391

---------------------------------------------------------------------------------------
| Id  | Operation                    | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |        |   250K|     9M|   500K  (1)| 01:40:03 |
|   1 |  NESTED LOOPS                |        |   250K|     9M|   500K  (1)| 01:40:03 |
|   2 |   NESTED LOOPS               |        |   250K|     9M|   500K  (1)| 01:40:03 |
|*  3 |    TABLE ACCESS FULL         | T1     |   250K|  7324K|    99  (30)| 00:00:02 |
|*  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 "SQLPROFILE_NAME1" used for this statement


Statistics
----------------------------------------------------------
        226  recursive calls
          0  db block gets
        434  consistent gets
          1  physical reads
          0  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
         27  sorts (memory)
          0  sorts (disk)
         36  rows processed

可以看到,Oracle优化器评估表T1经过Like条件过滤后返回的行数虽然很大,但是这里的执行计划仍然与未修改统计信息之前一样,使用range scan+ nested loop join。
Manual类型的sql profile可以起到很好的稳定目标SQL的执行计划的作用,不依赖统计信息,这一点是auto类型的sql profile所不具备的。
通过以上的测试,我们明白了DBMS_SQLTUNE.IMPORT_SQL_PROFILE的使用,同时也验证了这种方式的有效性,SQL Profiles能够像Outlines一样,能够稳定SQL的执行计划。

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

SQL> set autot off
SQL> select t1.*,t2.owner from t1,t2 where t1.object_name like '%T1%' and t1.object_id=t2.object_id;

36 rows selected.

SQL> select * from table(dbms_xplan.display_cursor(null,null,'OUTLINE'));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  4zbqykx89yc8v, 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             |        |       |       |  5071 (100)|          |
|   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 |
---------------------------------------------------------------------------------------

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脚本,coe_xfr_sql_profile.sql。这个脚本可以用于从shared pool、awr中提取指定的SQL ID的Outline Data并创建SQL Profile。后面演示。
我们可以对比下:
删除profile,走原来的执行计划,查看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")
      FULL(@"SEL$1" "T2"@"SEL$1")
      LEADING(@"SEL$1" "T1"@"SEL$1" "T2"@"SEL$1")
      USE_HASH(@"SEL$1" "T2"@"SEL$1")
      END_OUTLINE_DATA
  */

不同的执行计划,outline data信息是不同的,它是这条sql走这个执行计划,所隐藏的hint,所以不相同。

  1. coe_xfr_sql_profile脚本

1)构造2条sql

SQL> set pages 1000 lines 1000
SQL> select * from t2 where object_id=2;  --模拟的good sql,走索引

OWNER                          OBJECT_NAME                                                                                                                      SUBOBJECT_NAME           OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE          CREATED      LAST_DDL_TIM TIMESTAMP           STATUS  T G S  NAMESPACE EDITION_NAME
------------------------------ -------------------------------------------------------------------------------------------------------------------------------- ------------------------------ ---------- -------------- ------------------- ------------ ------------ ------------------- ------- - - - ---------- ------------------------------
SYS                            C_OBJ#                                                                                                                                                           2 CLUSTER      21-NOV-19    21-NOV-19    2019-11-21:11:44:16 VALID   N N N          5

SQL> select /*+ no_index(t2 T2_IDX)*/ * from t2 where object_id=2;  --模拟的bad sql,全表扫描

OWNER                          OBJECT_NAME                                                                                                                      SUBOBJECT_NAME           OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE          CREATED      LAST_DDL_TIM TIMESTAMP           STATUS  T G S  NAMESPACE EDITION_NAME
------------------------------ -------------------------------------------------------------------------------------------------------------------------------- ------------------------------ ---------- -------------- ------------------- ------------ ------------ ------------------- ------- - - - ---------- ------------------------------
SYS                            C_OBJ#                                                                                                                                                           2 CLUSTER      21-NOV-19    21-NOV-19    2019-11-21:11:44:16 VALID   N N N          5

SQL> set autot trace
SQL> select * from t2 where object_id=2;


Execution Plan
----------------------------------------------------------
Plan hash value: 4244861920

--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |     1 |    98 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T2     |     1 |    98 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | T2_IDX |     1 |       |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OBJECT_ID"=2)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
       1611  bytes sent via SQL*Net to client
        519  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL> select /*+ no_index(t2 T2_IDX)*/ * from t2 where object_id=2;


Execution Plan
----------------------------------------------------------
Plan hash value: 1513984157

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |    98 |   321   (1)| 00:00:04 |
|*  1 |  TABLE ACCESS FULL| T2   |     1 |    98 |   321   (1)| 00:00:04 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("OBJECT_ID"=2)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       1151  consistent gets
          0  physical reads
          0  redo size
       1608  bytes sent via SQL*Net to client
        519  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

--绑定必须依赖sqlid和plan hash value,可以如下查询:
SQL>select SQL_ID,PLAN_HASH_VALUE,sql_text,PARSE_CALLS,LOADS,EXECUTIONS from v$sql where SQL_TEXT like '%from t2%' and sql_text not like '%v$sql%';
SQL>col SQL_TEXT for a60
SQL>/

SQL_ID        PLAN_HASH_VALUE SQL_TEXT                                                     PARSE_CALLS      LOADS EXECUTIONS
------------- --------------- ------------------------------------------------------------ ----------- ---------- ----------
24328pmmhh5tp      1513984157 EXPLAIN PLAN SET STATEMENT_ID='PLUS490003' FOR select /*+ no           1          1          1
                              _index(t2 T2_IDX)*/ * from t2 where object_id=2

5u3pq1u6qj21n      4244861920 EXPLAIN PLAN SET STATEMENT_ID='PLUS490003' FOR select * from           1          1          1
                               t2 where object_id=2

189a578w09x47      4244861920 select * from t2 where object_id=2                                     4          1          4
18z48fk2jy688      1513984157 select /*+ no_index(t2 T2_IDX)*/ * from t2 where object_id=2           3          1          3

2)现在的任务就是把good sql的执行计划绑定到bad sql上面。
步骤如下:
在这里插入图片描述
我们已经模拟出来了good sql,也就是错误执行计划的sql加了hint后改写的。

SQL>@coe_xfr_sql_profile

Parameter 1:
SQL_ID (required)

Enter value for 1: 189a578w09x47


PLAN_HASH_VALUE AVG_ET_SECS
--------------- -----------
     4244861920        .001

Parameter 2:
PLAN_HASH_VALUE (required)

Enter value for 2: 4244861920

Values passed to coe_xfr_sql_profile:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SQL_ID         : "189a578w09x47"
PLAN_HASH_VALUE: "4244861920"

SQL>BEGIN
  2    IF :sql_text IS NULL THEN
  3      RAISE_APPLICATION_ERROR(-20100, 'SQL_TEXT for SQL_ID &&sql_id. was not found in memory (gv$sqltext_with_newlines) or AWR (dba_hist_sqltext).');
  4    END IF;
  5  END;
  6  /
SQL>SET TERM OFF;
SQL>BEGIN
  2    IF :other_xml IS NULL THEN
  3      RAISE_APPLICATION_ERROR(-20101, 'PLAN for SQL_ID &&sql_id. and PHV &&plan_hash_value. was not found in memory (gv$sql_plan) or AWR (dba_hist_sql_plan).');
  4    END IF;
  5  END;
  6  /
SQL>SET TERM OFF;

Execute coe_xfr_sql_profile_189a578w09x47_4244861920.sql
on TARGET system in order to create a custom SQL Profile
with plan 4244861920 linked to adjusted sql_text.


COE_XFR_SQL_PROFILE completed.
SQL>
SQL>@coe_xfr_sql_profile

Parameter 1:
SQL_ID (required)

Enter value for 1: 18z48fk2jy688


PLAN_HASH_VALUE AVG_ET_SECS
--------------- -----------
     1513984157        .028

Parameter 2:
PLAN_HASH_VALUE (required)

Enter value for 2: 1513984157

Values passed to coe_xfr_sql_profile:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SQL_ID         : "18z48fk2jy688"
PLAN_HASH_VALUE: "1513984157"

SQL>BEGIN
  2    IF :sql_text IS NULL THEN
  3      RAISE_APPLICATION_ERROR(-20100, 'SQL_TEXT for SQL_ID &&sql_id. was not found in memory (gv$sqltext_with_newlines) or AWR (dba_hist_sqltext).');
  4    END IF;
  5  END;
  6  /
SQL>SET TERM OFF;
SQL>BEGIN
  2    IF :other_xml IS NULL THEN
  3      RAISE_APPLICATION_ERROR(-20101, 'PLAN for SQL_ID &&sql_id. and PHV &&plan_hash_value. was not found in memory (gv$sql_plan) or AWR (dba_hist_sql_plan).');
  4    END IF;
  5  END;
  6  /
SQL>SET TERM OFF;

Execute coe_xfr_sql_profile_18z48fk2jy688_1513984157.sql
on TARGET system in order to create a custom SQL Profile
with plan 1513984157 linked to adjusted sql_text.


COE_XFR_SQL_PROFILE completed.

每条sql执行完脚本,都会生成一个文件,一个是目标sql的(good),一个是原sql的(bad)
编辑原sql的(bad)的生成文件,用目标sql的(good)的hint内容替换原sql的(bad)的内容,大概如下:

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'[INDEX_RS_ASC(@"SEL$1" "T2"@"SEL$1" ("T2"."OBJECT_ID"))]',
q'[END_OUTLINE_DATA]');

然后执行此文件:

SQL>@coe_xfr_sql_profile_18z48fk2jy688_1513984157.sql
SQL>REM
SQL>REM $Header: 215187.1 coe_xfr_sql_profile_18z48fk2jy688_1513984157.sql 11.4.4.4 2021/02/26 carlos.sierra $
SQL>REM
SQL>REM Copyright (c) 2000-2012, Oracle Corporation. All rights reserved.
SQL>REM
SQL>REM AUTHOR
SQL>REM   carlos.sierra@oracle.com
SQL>REM
SQL>REM SCRIPT
SQL>REM   coe_xfr_sql_profile_18z48fk2jy688_1513984157.sql
SQL>REM
SQL>REM DESCRIPTION
SQL>REM   This script is generated by coe_xfr_sql_profile.sql
SQL>REM   It contains the SQL*Plus commands to create a custom
SQL>REM   SQL Profile for SQL_ID 18z48fk2jy688 based on plan hash
SQL>REM   value 1513984157.
SQL>REM   The custom SQL Profile to be created by this script
SQL>REM   will affect plans for SQL commands with signature
SQL>REM   matching the one for SQL Text below.
SQL>REM   Review SQL Text and adjust accordingly.
SQL>REM
SQL>REM PARAMETERS
SQL>REM   None.
SQL>REM
SQL>REM EXAMPLE
SQL>REM   SQL> START coe_xfr_sql_profile_18z48fk2jy688_1513984157.sql;
SQL>REM
SQL>REM NOTES
SQL>REM   1. Should be run as SYSTEM or SYSDBA.
SQL>REM   2. User must have CREATE ANY SQL PROFILE privilege.
SQL>REM   3. SOURCE and TARGET systems can be the same or similar.
SQL>REM   4. To drop this custom SQL Profile after it has been created:
SQL>REM  EXEC DBMS_SQLTUNE.DROP_SQL_PROFILE('coe_18z48fk2jy688_1513984157');
SQL>REM   5. Be aware that using DBMS_SQLTUNE requires a license
SQL>REM  for the Oracle Tuning Pack.
SQL>REM   6. If you modified a SQL putting Hints in order to produce a desired
SQL>REM  Plan, you can remove the artifical Hints from SQL Text pieces below.
SQL>REM  By doing so you can create a custom SQL Profile for the original
SQL>REM  SQL but with the Plan captured from the modified SQL (with Hints).
SQL>REM
SQL>WHENEVER SQLERROR EXIT SQL.SQLCODE;
SQL>REM
SQL>VAR signature NUMBER;
SQL>VAR signaturef NUMBER;
SQL>REM
SQL>DECLARE
  2  sql_txt CLOB;
  3  h       SYS.SQLPROF_ATTR;
  4  PROCEDURE wa (p_line IN VARCHAR2) IS
  5  BEGIN
  6  DBMS_LOB.WRITEAPPEND(sql_txt, LENGTH(p_line), p_line);
  7  END wa;
  8  BEGIN
  9  DBMS_LOB.CREATETEMPORARY(sql_txt, TRUE);
 10  DBMS_LOB.OPEN(sql_txt, DBMS_LOB.LOB_READWRITE);
 11  -- SQL Text pieces below do not have to be of same length.
 12  -- So if you edit SQL Text (i.e. removing temporary Hints),
 13  -- there is no need to edit or re-align unmodified pieces.
 14  wa(q'[select /*+ no_index(t2 T2_IDX)*/ * from t2 where object_id=2]');
 15  DBMS_LOB.CLOSE(sql_txt);
 16  h := SYS.SQLPROF_ATTR(
 17  q'[BEGIN_OUTLINE_DATA]',
 18  q'[IGNORE_OPTIM_EMBEDDED_HINTS]',
 19  q'[OPTIMIZER_FEATURES_ENABLE('11.2.0.4')]',
 20  q'[DB_VERSION('11.2.0.4')]',
 21  q'[ALL_ROWS]',
 22  q'[OUTLINE_LEAF(@"SEL$1")]',
 23  q'[INDEX_RS_ASC(@"SEL$1" "T2"@"SEL$1" ("T2"."OBJECT_ID"))]',
 24  q'[END_OUTLINE_DATA]');
 25  :signature := DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE(sql_txt);
 26  :signaturef := DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE(sql_txt, TRUE);
 27  DBMS_SQLTUNE.IMPORT_SQL_PROFILE (
 28  sql_text    => sql_txt,
 29  profile     => h,
 30  name        => 'coe_18z48fk2jy688_1513984157',
 31  description => 'coe 18z48fk2jy688 1513984157 '||:signature||' '||:signaturef||'',
 32  category    => 'DEFAULT',
 33  validate    => TRUE,
 34  replace     => TRUE,
 35  force_match => TRUE /* TRUE:FORCE (match even when different literals in SQL). FALSE:EXACT (similar to CURSOR_SHARING) */ );
 36  DBMS_LOB.FREETEMPORARY(sql_txt);
 37  END;
 38  /

PL/SQL procedure successfully completed.

SQL>WHENEVER SQLERROR CONTINUE
SQL>SET ECHO OFF;

            SIGNATURE
---------------------
  8178536432892493039


           SIGNATUREF
---------------------
 10922292449146850743


... manual custom SQL Profile has been created


COE_XFR_SQL_PROFILE_18z48fk2jy688_1513984157 completed

验证:

SQL> select /*+ no_index(t2 T2_IDX)*/ * from t2 where object_id=2;


Execution Plan
----------------------------------------------------------
Plan hash value: 4244861920

--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |     1 |    98 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T2     |     1 |    98 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | T2_IDX |     1 |       |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OBJECT_ID"=2)

Note
-----
   - SQL profile "coe_18z48fk2jy688_1513984157" used for this statement


Statistics
----------------------------------------------------------
        256  recursive calls
          2  db block gets
        249  consistent gets
          1  physical reads
          0  redo size
       1611  bytes sent via SQL*Net to client
        519  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          1  rows processed

从上述显示内容中可以看到,新生成的manual类型的sql profile(名为coe_18z48fk2jy688_1513984157)已经生效了,原目标SQL的执行计划也从原先的对T2的全表扫描变为了对索引T2_IDX 的
索引范围扫描,这也同时说明Manual类型的sql profile确实可以在不改变目标SQL的SQL文本的情况下更改其执行计划。

  1. 脚本coe_load_sql_profile

为了避免影响,先删除原来的sql profile

session 2:
SQL> exec dbms_sqltune.drop_sql_profile('coe_18z48fk2jy688_1513984157'); 

PL/SQL procedure successfully completed.

查询sql id

SQL> select SQL_ID,PLAN_HASH_VALUE,sql_text,PARSE_CALLS,LOADS,EXECUTIONS from v$sql where SQL_TEXT like '%from t2%' and sql_text not like '%v$sql%';

SQL_ID        PLAN_HASH_VALUE SQL_TEXT                                                     PARSE_CALLS      LOADS EXECUTIONS
------------- --------------- ------------------------------------------------------------ ----------- ---------- ----------
189a578w09x47      4244861920 select * from t2 where object_id=2                                     1          1          1
18z48fk2jy688      1513984157 select /*+ no_index(t2 T2_IDX)*/ * from t2 where object_id=2           1          2          1

执行脚本:

SQL> @coe_load_sql_profile

Parameter 1:
ORIGINAL_SQL_ID (required)

Enter value for 1: 18z48fk2jy688    --bad plan

Parameter 2:
MODIFIED_SQL_ID (required)

Enter value for 2: 189a578w09x47    --good plan


     PLAN_HASH_VALUE          AVG_ET_SECS
-------------------- --------------------
          4244861920                 .003

Parameter 3:
PLAN_HASH_VALUE (required)

Enter value for 3: 4244861920     --good plan hash value

Values passed to coe_load_sql_profile:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ORIGINAL_SQL_ID: "18z48fk2jy688"
MODIFIED_SQL_ID: "189a578w09x47"
PLAN_HASH_VALUE: "4244861920"

SQL>BEGIN
  2    IF :sql_text IS NULL THEN
  3      RAISE_APPLICATION_ERROR(-20100, 'SQL_TEXT for original SQL_ID &&original_sql_id. was not found in memory (gv$sqltext_with_newlines) or AWR (dba_hist_sqltext).');
  4    END IF;
  5  END;
  6  /
SQL>SET TERM OFF;
SQL>BEGIN
  2    IF :other_xml IS NULL THEN
  3      RAISE_APPLICATION_ERROR(-20101, 'PLAN for modified SQL_ID &&modified_sql_id. and PHV &&plan_hash_value. was not found in memory (gv$sql_plan) or AWR (dba_hist_sql_plan).');
  4    END IF;
  5  END;
  6  /
SQL>
SQL>SET ECHO OFF;
0001 BEGIN_OUTLINE_DATA
0002 IGNORE_OPTIM_EMBEDDED_HINTS
0003 OPTIMIZER_FEATURES_ENABLE('11.2.0.4')
0004 DB_VERSION('11.2.0.4')
0005 ALL_ROWS
0006 OUTLINE_LEAF(@"SEL$1")
0007 INDEX_RS_ASC(@"SEL$1" "T2"@"SEL$1" ("T2"."OBJECT_ID"))
0008 END_OUTLINE_DATA

验证:

session 1:
SQL> /


Execution Plan
----------------------------------------------------------
Plan hash value: 4244861920

--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |     1 |    98 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T2     |     1 |    98 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | T2_IDX |     1 |       |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OBJECT_ID"=2)

Note
-----
   - SQL profile "18Z48FK2JY688_4244861920" used for this statement


Statistics
----------------------------------------------------------
       1048  recursive calls
          0  db block gets
        519  consistent gets
          1  physical reads
          0  redo size
       1611  bytes sent via SQL*Net to client
        519  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
         20  sorts (memory)
          0  sorts (disk)
          1  rows processed
  1. 极简版
    如果客户是内网环境,不让传输脚本,脚本内容很多,手写不现实,下面就是上面脚本的核心内容,比较短小精悍,可以手写,直接使用。
    – 给特定SQL绑上改写后的特定执行计划,需要事先查询得到对应的sql_id 和 plan_hash_value, COE_profile.sql的极简版
    declare
    v_hint SYS.SQLPROF_ATTR;
    v_sql_text clob;
    begin
    select sql_fulltext into v_sql_text from vKaTeX parse error: Expected 'EOF', got '&' at position 21: …here sql_id = '&̲bad_sqlid' and …sql_plan
    where sql_id = ‘&good_sqlid’
    and other_xml is not null)) d;
    dbms_sqltune.import_sql_profile(
    v_sql_text,
    v_hint,
    ‘sql_profile_&bad_sqlid’,
    force_match=>true,replace=>true
    );
    end;
    /

删除原先的

SQL> exec dbms_sqltune.drop_sql_profile('18Z48FK2JY688_4244861920'); 

PL/SQL procedure successfully completed.

查询sql id:

SQL> select SQL_ID,PLAN_HASH_VALUE,sql_text,PARSE_CALLS,LOADS,EXECUTIONS from v$sql where SQL_TEXT like '%from t2%' and sql_text not like '%v$sql%';

SQL_ID        PLAN_HASH_VALUE SQL_TEXT                                                     PARSE_CALLS      LOADS EXECUTIONS
------------- --------------- ------------------------------------------------------------ ----------- ---------- ----------
4y10hb0u8tpft      1513984157 EXPLAIN PLAN SET STATEMENT_ID='PLUS580003' FOR select /*+ no           1          2          1
                              _index(t2 T2_IDX)*/ * from t2 where object_id=2

189a578w09x47      4244861920 select * from t2 where object_id=2                                     1          2          1
18z48fk2jy688      1513984157 select /*+ no_index(t2 T2_IDX)*/ * from t2 where object_id=2           2          3          2

执行脚本:

SQL> declare
  2  v_hint SYS.SQLPROF_ATTR;
  3  v_sql_text clob;
  4  begin
  5  select sql_fulltext into v_sql_text from v$sql where sql_id = '&bad_sqlid' and rownum=1;
  6  select extractvalue(value(d), '/hint') as outline_hints bulk collect  
  7    into v_hint  
  8    from xmltable('/*/outline_data/hint' passing  
  9                  (select xmltype(other_xml) as xmlval  
 10                     from v$sql_plan  
 11                    where sql_id = '&good_sqlid'  
 12                      and other_xml is not null)) d; 
 13  dbms_sqltune.import_sql_profile(
 14  v_sql_text,
 15  v_hint,
 16  'sql_profile_&bad_sqlid',
 17  force_match=>true,replace=>true
 18  );
 19  end;
 20  /
Enter value for bad_sqlid: 18z48fk2jy688
old   5: select sql_fulltext into v_sql_text from v$sql where sql_id = '&bad_sqlid' and rownum=1;
new   5: select sql_fulltext into v_sql_text from v$sql where sql_id = '18z48fk2jy688' and rownum=1;
Enter value for good_sqlid: 189a578w09x47
old  11:                   where sql_id = '&good_sqlid'
new  11:                   where sql_id = '189a578w09x47'
Enter value for bad_sqlid: 18z48fk2jy688
old  16: 'sql_profile_&bad_sqlid',
new  16: 'sql_profile_18z48fk2jy688',

PL/SQL procedure successfully completed.

验证:

session 1:

SQL> /


Execution Plan
----------------------------------------------------------
Plan hash value: 4244861920

--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |     1 |    98 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T2     |     1 |    98 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | T2_IDX |     1 |       |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OBJECT_ID"=2)

Note
-----
   - SQL profile "sql_profile_18z48fk2jy688" used for this statement   --已变化


Statistics
----------------------------------------------------------
          7  recursive calls
          0  db block gets
          9  consistent gets
          1  physical reads
          0  redo size
       1611  bytes sent via SQL*Net to client
        519  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

manual绑定,其实就是加hint,为了方便,其实就是拿脚本绑定,有3个脚本:
1、极简版
2、coe_xfr_sql_profile
3、coe_load_sql_profile
coe_xfr_sql_profile.sql脚本来固定sql 执行计划,但是这个脚本操作起来比较麻烦,而且容易出错。这个脚本的正确用途是用来做不同数据库之间sql执行计划的固定。
最方便的脚本是:coe_load_sql_profile.sql,使用这个脚本,只需要输入几个参数,就能完成快速恢复执行计划的任务。
但是也分为2中情况,一是1个sql,有多个执行计划,即sql_id相同,但是plan hash value不同,拿好的plan hash value去绑定坏的plan hash value;
二是,为了生成最优的执行计划,人为给原sql加了hint,生成新的sql,生成新的执行计划,拿好的sql_id和plan hash value去绑定坏的sql_id

绑定不存在的执行计划–绑定加hint执行计划

declare
v_hints sys.sqlprof_attr;
hint varchar2(100);
v_sql_id varchar2(100);
v_sql_fulltext varchar2(32767);
begin
v_sql_id := ‘&请输入SQL_ID’;
hint := ‘&请输入HINT’;
select sql_fulltext
into v_sql_fulltext
from v$sql
where sql_id = v_sql_id
and rownum = 1;
v_hints := sys.sqlprof_attr(hint);
dbms_sqltune.import_sql_profile(v_sql_fulltext,
v_hints,
v_sql_id, —SQL PROFILE 名字,这里我用sql_id来命名
force_match => true);
end;
/

session 2:
SQL> select SQL_ID,sql_text,PARSE_CALLS,LOADS,EXECUTIONS from v$sql where SQL_TEXT like '%from t1%' and sql_text not like '%v$sql%';
SQL_ID        SQL_TEXT                                                     PARSE_CALLS      LOADS EXECUTIONS
------------- ------------------------------------------------------------ ----------- ---------- ----------

4zbqykx89yc8v select t1.*,t2.owner from t1,t2 where t1.object_name like '%           3          3          3
              T1%' and t1.object_id=t2.object_id

SQL> declare
  2    v_hints        sys.sqlprof_attr;
  3    hint           varchar2(100);
  4    v_sql_id       varchar2(100);
  5    v_sql_fulltext varchar2(32767);
  6  begin
  7    v_sql_id := '&请输入SQL_ID';
  8    hint     := '&请输入HINT';
  9    select sql_fulltext
 10      into v_sql_fulltext
 11      from v$sql
 12     where sql_id = v_sql_id
 13       and rownum = 1;
 14    v_hints := sys.sqlprof_attr(hint); 
 15    dbms_sqltune.import_sql_profile(v_sql_fulltext, 
 16                                    v_hints,
 17                                    v_sql_id, ---SQL PROFILE 名字,这里我用sql_id来命名
 18                                    force_match => true);
 19  end;
 20  /
Enter value for 请输入sql_id: 4zbqykx89yc8v
old   7:   v_sql_id := '&请输入SQL_ID';
new   7:   v_sql_id := '4zbqykx89yc8v';
Enter value for 请输入hint: USE_NL(T1@SEL$1 T2@SEL$1) INDEX(T2@SEL$1)
old   8:   hint     := '&请输入HINT';
new   8:   hint     := 'USE_NL(T1@SEL$1 T2@SEL$1) INDEX(T2@SEL$1)';

PL/SQL procedure successfully completed.

SQL> select COMP_DATA from dba_sql_profiles a, SQLOBJ$DATA b where a.signature = b.signature and a.name='SQLPROFILE_NAME1';  

no rows selected

SQL> select COMP_DATA from dba_sql_profiles a, SQLOBJ$DATA b where a.signature = b.signature and a.name='4zbqykx89yc8v';

COMP_DATA
--------------------------------------------------------------------------------
<outline_data><hint><![CDATA[USE_NL(T1@SEL$1 T2@SEL$1) INDEX(T2@SEL$1)]]></hint>

session 1:
SQL> set autot off
SQL> set heading off
SQL> set echo off
SQL> select * from table(dbms_xplan.display_cursor('4zbqykx89yc8v',null,'OUTLINE'));

SQL_ID  4zbqykx89yc8v, 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             |        |       |       |  5071 (100)|          |
|   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 |
---------------------------------------------------------------------------------------

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 4zbqykx89yc8v used for this statement


47 rows selected.

可以看出来已经生效,但是此hint要自己判断,自己书写,难度较大。

总结

  1. manual类型的sql profile本质上就是一堆hint的组合,这一堆hint的组合实际上来源于执行计划中outline data部分的hint组合。manual类型的sql profile同样可以在不更改目标sql的sql文本的情况下调整其执行计划,而且更为重要的是,manual类型的sql profile可以起到很好的稳定目标sql的执行计划的作用,这一点是automatic类型的sql profile所不具备的。
  2. 最方便的脚本是:coe_load_sql_profile.sql,使用这个脚本,只需要输入几个参数,就能完成快速恢复执行计划的任务。原理就是从v$sql_plan中的other_xml中提取出profile用到的属性,并使用dbms_sqltune.import_sql_profile导入但是也分为2中情况:
    一是1个sql,有多个执行计划,即sql_id相同,但是plan hash value不同,拿好的plan hash value去绑定坏的plan hash value;
    二是,为了生成最优的执行计划,人为给原sql加了hint,生成新的sql,生成新的执行计划,拿好的sql_id和plan hash value去绑定坏的sql_id。
  3. 各种方案适用的场景不同,如下:

在这里插入图片描述 4. 看来SQL Profile可以接受常规的hint ,只不过这些hint要包含Query Block Name,如果SQL Profile发现指定的hint无效,会简单的忽略掉这些hint,不会报任何的错误,也不会做任何的校验。既然常规的hint可以对SQL Profile起作用,那么我们也可以用SQL Profile来锁定执行计划了。从上面的执行计划输出也可以看到由于我们使用了常规的hint,因此执行计划的基数信息并没有得到纠正,仅仅是通过这种暴力的hint把执行计划强制修正为索引扫描了。

  1. 参考:
    Query Block Name
    手工创建SQL profile
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值