修正执行计划的利器,SQL PROFILE脚本

点击这里下载 sql profile 脚本 profile.sql

我们先来看下如何手工创建sql profile来修正执行计划。
创建两张表test1、test2,在表test2的列object_id 上创建索引,分析两张表。

create table test1 as select * from dba_objects;

create table test2 as select * from dba_objects;

create index test2_oi_ind on test2(object_id);

begin

  dbms_stats.gather_table_stats(ownname          =>'test',

                                tabname          => 'test1',

                                no_invalidate    => FALSE,

                                estimate_percent => 100,

                                force            => true,

                                degree         => 5,

                                method_opt       => 'for  all columns  size 1',

                                cascade          => true);

end;

/

 

begin

  dbms_stats.gather_table_stats(ownname          =>'test',

                                tabname          => 'test2',

                                no_invalidate    => FALSE,

                                estimate_percent => 100,

                                force            => true,

                                degree         => 5,

                                method_opt       => 'for  all columns  size 1',

                                cascade          => true);

end;

/

然后看看如下一个SQL:

select test2.object_name, test2.object_type
  from test1, test2
 where test1.object_type like '%RULE'
   and test1.object_id = test2.object_id;

 

OBJECT_NAME          OBJECT_TYPE

-------------------- --------------------------------------

ALERT_QUE$1          RULE

 

1 row selected.

默认的执行计划为hash join:

Plan hash value: 497311279

 

----------------------------------------------------------------------------

| Id  | Operation          | Name  | Rows  | Bytes | Cost (%CPU)| Time     |

----------------------------------------------------------------------------

|   0 | SELECT STATEMENT   |       |   774 | 33282 |   100   (2)| 00:00:02 |

|*  1 |  HASH JOIN         |       |   774 | 33282 |   100   (2)| 00:00:02 |

|*  2 |   TABLE ACCESS FULL| TEST1 |   774 |  9288 |    50   (2)| 00:00:01 |

|   3 |   TABLE ACCESS FULL| TEST2 | 15478 |   468K|    49   (0)| 00:00:01 |

----------------------------------------------------------------------------

 

Predicate Information (identified by operation id):

---------------------------------------------------

 

   1 - access("TEST1"."OBJECT_ID"="TEST2"."OBJECT_ID")

   2 - filter("TEST1"."OBJECT_TYPE" LIKE '%RULE' AND

              "TEST1"."OBJECT_TYPE" IS NOT NULL)

如果我们想让执行计划走NEST LOOP JOIN,而不是优化器选用的HASH JOIN,该如何办?

步骤一:查找到你的查询块的名称

SELECT operation,options,object_name,object_alias

FROM v$sql_plan

WHERE sql_id='7uga1u1twnqw1'

AND child_number=0

/

OPERATION                 OPTIONS              OBJECT_NAME          OBJECT_ALIAS

------------------------- -------------------- -------------------- --------------------

SELECT STATEMENT

NESTED LOOPS

NESTED LOOPS

TABLE ACCESS              FULL                 TEST1                TEST1@SEL$1

INDEX                     RANGE SCAN           TEST2_OI_IND         TEST2@SEL$1

TABLE ACCESS              BY INDEX ROWID       TEST2                TEST2@SEL$1

也可以通过select * from table(dbms_xplan.display_cursor(sql_id,null,'outline'))来查看Outline Data来获取查询块。我更喜欢这种方法。

步骤二:构建你的HINT

leading(@"SEL$1" TEST1@"SEL$1") use_nl(@"SEL$1" TEST2@"SEL$1")

步骤三:创建你的SQL PROFILE

declare

l_profile_name varchar2(30);

cl_sql_text clob;

begin

 

select

sql_fulltext

into

cl_sql_text

from

v$sqlarea

where

sql_id = '7uga1u1twnqw1';

 

dbms_sqltune.import_sql_profile(

sql_text => cl_sql_text,

profile => sqlprof_attr(q'[leading(@"SEL$1" TEST1@"SEL$1") use_nl(@"SEL$1" TEST2@"SEL$1")]'),

category => '',

name => 'dwrose_xxx',

force_match =>FALSE

);

end;

/

步骤四,确认你的执行计划已经按照要求被改变:

select test2.object_name,test2.object_type from test1,test2 where test1.object_type like '%RULE' and test1.object_id=test2.object_id;

OBJECT_NAME          OBJECT_TYPE

-------------------- --------------------------------------

ALERT_QUE$1          RULE

sys@DLSP>select * from table(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT

-----------------------------------------------------------------------------------------------------------------------------------------------------------

SQL_ID  bhm28h5575bjy, child number 0

-------------------------------------

select test2.object_name,test2.object_type from test1,test2 where

test1.object_type like '%RULE' and test1.object_id=test2.object_id

 

Plan hash value: 800282841

 

---------------------------------------------------------------------------------------------

| Id  | Operation                    | Name         | Rows  | Bytes | Cost (%CPU)| Time     |

---------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT             |              |       |       |  1598 (100)|          |

|   1 |  NESTED LOOPS                |              |       |       |            |          |

|   2 |   NESTED LOOPS               |              |   774 | 33282 |  1598   (1)| 00:00:20 |

|*  3 |    TABLE ACCESS FULL         | TEST1        |   774 |  9288 |    50   (2)| 00:00:01 |

|*  4 |    INDEX RANGE SCAN          | TEST2_OI_IND |     1 |       |     1   (0)| 00:00:01 |

|   5 |   TABLE ACCESS BY INDEX ROWID| TEST2        |     1 |    31 |     2   (0)| 00:00:01 |

---------------------------------------------------------------------------------------------

 

Predicate Information (identified by operation id):

---------------------------------------------------

 

   3 - filter(("TEST1"."OBJECT_TYPE" LIKE '%RULE' AND "TEST1"."OBJECT_TYPE" IS NOT

              NULL))

   4 - access("TEST1"."OBJECT_ID"="TEST2"."OBJECT_ID")

 

Note

-----

   - SQL profile dwrose_xxx used for this statement

通过执行计划输出的Note部分可以看到SQL PROFILE已经被使用。

OK,看我们通过如何通过sql profile脚本把执行计划修正为NEST LOOP JOIN,这个过程将变得更加简单:


>@profile

Enter value for sql_id: bhm28h5575bjy          -------------第一个步骤:输入SQL_ID

 

PLAN_TABLE_OUTPUT

-----------------------------------------------------------------------------------------------

SQL_ID  bhm28h5575bjy, child number 0

-------------------------------------

select test2.object_name,test2.object_type from test1,test2 where

test1.object_type like '%RULE' and test1.object_id=test2.object_id

 

Plan hash value: 497311279

 

----------------------------------------------------------------------------

| Id  | Operation          | Name  | Rows  | Bytes | Cost (%CPU)| Time     |

----------------------------------------------------------------------------

|   0 | SELECT STATEMENT   |       |       |       |   100 (100)|          |

|*  1 |  HASH JOIN         |       |   774 | 33282 |   100   (2)| 00:00:02 |

|*  2 |   TABLE ACCESS FULL| TEST1 |   774 |  9288 |    50   (2)| 00:00:01 |

|   3 |   TABLE ACCESS FULL| TEST2 | 15479 |   468K|    49   (0)| 00:00:01 |

----------------------------------------------------------------------------

 

Outline Data

-------------

 

  /*+

      BEGIN_OUTLINE_DATA

      IGNORE_OPTIM_EMBEDDED_HINTS

      OPTIMIZER_FEATURES_ENABLE('11.2.0.3')

      DB_VERSION('11.2.0.3')

      OPT_PARAM('_optim_peek_user_binds' 'false')

      OPT_PARAM('_optimizer_skip_scan_enabled' 'false')

      ALL_ROWS

      OUTLINE_LEAF(@"SEL$1")

      FULL(@"SEL$1" "TEST1"@"SEL$1")

      FULL(@"SEL$1" "TEST2"@"SEL$1")

      LEADING(@"SEL$1" "TEST1"@"SEL$1" "TEST2"@"SEL$1")

      USE_HASH(@"SEL$1" "TEST2"@"SEL$1")

      END_OUTLINE_DATA

  */

 

Predicate Information (identified by operation id):

---------------------------------------------------

 

   1 - access("TEST1"."OBJECT_ID"="TEST2"."OBJECT_ID")

   2 - filter(("TEST1"."OBJECT_TYPE" LIKE '%RULE' AND

              "TEST1"."OBJECT_TYPE" IS NOT NULL))

 

 

42 rows selected.

------------第二个步骤:输入你构造好的HINT,可以写多个,以空格隔开

Enter value for hint_text: LEADING(@"SEL$1" "TEST1"@"SEL$1" "TEST2"@"SEL$1") USE_NL(@"SEL$1" "TEST2"@"SEL$1")   
     

Profile profile_bhm28h5575bjy_dwrose created.

需要强调的是,根据上面脚本的输出可看到,Outline Data部分已经为你提供了当前查询计划的HINT,你可以通过改造这些HINT来为新的执行计划所用。例如,我们想让查询走NEST LOOP JOIN,如果是正常的不带查询块的HINT,写法为leading(test1) use_nl(test2),带上查询块通过参考Outline Data部分提供的HINT,写法为:
LEADING(@"SEL$1" "TEST1"@"SEL$1" "TEST2"@"SEL$1") USE_NL(@"SEL$1" "TEST2"@"SEL$1")

我们来看看是否执行计划已经被修正:

test@DLSP>select test2.object_name,test2.object_type from test1,test2 where test1.object_type like '%RULE' and test1.object_id=test2.object_id;

 

OBJECT_NAME          OBJECT_TYPE

-------------------- --------------------------------------

ALERT_QUE$1          RULE

1 row selected.

test@DLSP>select * from table(dbms_xplan.display_cursor);

 

PLAN_TABLE_OUTPUT

-----------------------------------------------------------------------------------------------

SQL_ID  bhm28h5575bjy, child number 0

-------------------------------------

select test2.object_name,test2.object_type from test1,test2 where

test1.object_type like '%RULE' and test1.object_id=test2.object_id

 

Plan hash value: 800282841

 

---------------------------------------------------------------------------------------------

| Id  | Operation                    | Name         | Rows  | Bytes | Cost (%CPU)| Time     |

---------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT             |              |       |       |  1598 (100)|          |

|   1 |  NESTED LOOPS                |              |       |       |            |          |

|   2 |   NESTED LOOPS               |              |   774 | 33282 |  1598   (1)| 00:00:20 |

|*  3 |    TABLE ACCESS FULL         | TEST1        |   774 |  9288 |    50   (2)| 00:00:01 |

|*  4 |    INDEX RANGE SCAN          | TEST2_OI_IND |     1 |       |     1   (0)| 00:00:01 |

|   5 |   TABLE ACCESS BY INDEX ROWID| TEST2        |     1 |    31 |     2   (0)| 00:00:01 |

---------------------------------------------------------------------------------------------

 

Predicate Information (identified by operation id):

---------------------------------------------------

 

   3 - filter(("TEST1"."OBJECT_TYPE" LIKE '%RULE' AND "TEST1"."OBJECT_TYPE" IS NOT

              NULL))

   4 - access("TEST1"."OBJECT_ID"="TEST2"."OBJECT_ID")

 

Note

-----

   - SQL profile profile_bhm28h5575bjy_dwrose used for this statement

可以看到执行计划已经被固定为我们需要的NEST LOOP JOIN方式了。Note部分也已经提示这个SQL已经使用到了我们所创建的SQL PROFILE了。

实在是太easy了,只需要两个步骤就可以去修正一个SQL的执行计划了!!

SQL PROFILE脚本如下,enjoy it !!:

SET linesize 153

set verify off

SET pagesize 10000

accept sql_id -

prompt 'Enter value for sql_id: ' -

default 'dwrose'

 

SELECT * FROM TABLE(dbms_xplan.display_cursor('&&sql_id',NULL,'outline'));

 

accept hint_txt -

prompt 'Enter value for hint_text: ' -

default 'comment'

set feedback off

set sqlblanklines on

set serveroutput on

declare

l_profile_name varchar2(30);

cl_sql_text clob;

begin

 

select

sql_fulltext

into

cl_sql_text

from

v$sqlarea

where

sql_id = '&&sql_id';

 

 

select 'profile_'||'&&sql_id'||'_dwrose'

into l_profile_name

from dual;

 

dbms_sqltune.import_sql_profile(

sql_text => cl_sql_text,

profile => sqlprof_attr(q'[&&hint_txt]'),

category => '',

name => l_profile_name,

force_match =>FALSE

);

 

dbms_output.put_line(' ');

dbms_output.put_line('Profile '||l_profile_name||' created.');

dbms_output.put_line(' ');

end;

/

 

col FIRST_LOAD_TIME for a20

select OBJECT_STATUS,FIRST_LOAD_TIME,plan_hash_value,executions,buffer_gets,LAST_ACTIVE_TIME from v$sql where sql_id='&&sql_id';

pro

pro

undef sql_id

undef hint_txt

 

set sqlblanklines off

set feedback on

时间: 2024-11-09 03:03:08

修正执行计划的利器,SQL PROFILE脚本的相关文章

【MS SQL】通过执行计划来分析SQL性能

原文:[MS SQL]通过执行计划来分析SQL性能 如何知道一句SQL语句的执行效率呢,只知道下面3种: 1.通过SQL语句执行时磁盘的活动量(IO)信息来分析:SET STATISTICS IO ON (开启) / SET STATISTICS IO OFF (关闭) 2.通过SQL语句执行时语法分析.编译以及执行所消耗的时间:SET STATISTICS TIME ON (开启) / SET STATISTICS TIME OFF (关闭) 3.通过执行计划查看:Ctrl + L  ----

决定一个SQL执行效率的是执行计划, 而不是SQL的写法

决定一个SQL执行效率的是执行计划, 而不是SQL的写法 1. 数据真实的"统计"的分布情况2. 系统视图中记录的统计信息3. 实际每个SQL执行时对应的数据情况 ------------------注意--------------------              信息统计很重要!

Sql Server中如何通过执行计划来分析SQL性能

如何知道一句SQL语句的执行效率呢,只知道下面3种: 1.通过SQL语句执行时磁盘的活动量(IO)信息来分析:SET STATISTICS IO ON (开启) / SET STATISTICS IO OFF(关闭) 2.通过SQL语句执行时语法分析.编译以及执行所消耗的时间:SET STATISTICS TIME ON (开启) / SET STATISTICS TIME OFF(关闭) 3.通过执行计划查看:Ctrl + L ---------------------------------

一个执行计划异常变更的案例 - 外传之SQL Profile(上)

之前的几篇文章: <一个执行计划异常变更的案例 - 前传> <一个执行计划异常变更的案例 - 外传之绑定变量窥探> <一个执行计划异常变更的案例 - 外传之查看绑定变量值的几种方法> <一个执行计划异常变更的案例 - 外传之rolling invalidation> <一个执行计划异常变更的案例 - 外传之聚簇因子(Clustering Factor)> <一个执行计划异常变更的案例 - 外传之查询执行计划的几种方法> <一个执

【SPM】Oracle如何固定执行计划

[SPM]Oracle如何固定执行计划   1.1  BLOG文档结构图   1.2  前言部分   1.2.1  导读和注意事项 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你所不知道的知识,~O(∩_∩)O~: ① 固定执行计划的常用方法:outline.SQL Profile.SPM(重点) ② coe_xfr_sql_profile.sql脚本的使用     Tips:        ① 若文章代码格式有错乱,推荐使用QQ.搜狗或360浏览器,也可以下载pdf格式

[20130123]spm与sql profile的主要区别在那里.txt

[20130123]spm与sql profile的主要区别在那里.txt     SPM是11G的新特性,而10g下sql profile一定程度代替store ountlined来稳定执行计划,oracle为什么11G下还要推出SPM?两者区别主要在那里呢? 我举一个例子来说明: 1.建立测试环境: select * from v$version where rownum BANNER -----------------------------------------------------

RDS SQL Server - 专题分享 - 巧用执行计划缓存之Table Scan

背景引入 执行计划中的Table Scan或者是Clustered Index Scan会导致非常低下的查询性能,尤其是对于大表或者超大表.执行计划缓存是SQL Server内存管理中非常重要的特性,这篇系列文章我们探讨如何从执行计划缓存的角度来发现RDS SQL数据库引擎中的Table Scan行为,以及与之相应SQL查询语句详细信息. 问题分析 其实,我们大家都知道,Table Scan或者Clustered Index Scan是关系型数据库查询性能很差的一种表扫描查询方式,如果在数据库引

SQL参数化查询的另一个理由 命中执行计划_MsSql

1概述 SQL语言的本质就是一串伪代码,表达的是做什么,而不是怎么做的意思.如其它语言一样,SQL语句需要编译之后才能运行,所以每一条SQL是需要通过编译器解释才能运行的(在这之间还要做SQL的优化).而这些步骤都是需要运行成本,所以在数据库中有一个叫做执行计划的东西,编译器会将编译过后的SQL存入执行计划当中,当遇到同样的SQL时,就直接调用执行计划来执行,而不需要再次编译. 通过对上面执行计划的认识,为了提高数据库运行的效率,我们需要尽可能的命中执行计划,这样就可以节省运行时间. 2相关SQ

SQL参数化查询的另一个理由 命中执行计划

1概述 SQL语言的本质就是一串伪代码,表达的是做什么,而不是怎么做的意思.如其它语言一样,SQL语句需要编译之后才能运行,所以每一条SQL是需要通过编译器解释才能运行的(在这之间还要做SQL的优化).而这些步骤都是需要运行成本,所以在数据库中有一个叫做执行计划的东西,编译器会将编译过后的SQL存入执行计划当中,当遇到同样的SQL时,就直接调用执行计划来执行,而不需要再次编译. 通过对上面执行计划的认识,为了提高数据库运行的效率,我们需要尽可能的命中执行计划,这样就可以节省运行时间. 2相关SQ