DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE手工替换绑定执行计划

本文介绍如何使用DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE来手工替换绑定执行计划,通过构造正确的执行计划并记录sql_id和plan_hash_value,然后导入到基线中并修改属性,最终使人工构造的执行计划生效。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE手工替换绑定执行计划
使用SPM与sql profile一样功能:通过手工构造执行计划替换绑定

--创建测试表
SQL> create table tb1 as select * from dba_objects where object_id is not null;

Table created.


SQL> select count(1) from tb1;

  COUNT(1)
----------
     72533


SQL> update tb1 set object_id=66 where rownum<72533;

72532 rows updated.

SQL> commit;

Commit complete.

SQL> update tb1 set object_id=77 where object_id<>66;

1 row updated.

SQL> commit;

Commit complete.


SQL> create index idx_tb1_object_id on tb1(object_id);

Index created.

SQL> exec dbms_stats.gather_table_stats('AIKI','TB1',cascade=>true,method_opt=>'for columns object_id  size 1');

PL/SQL procedure successfully completed.

--77只有1条,执行计划为全表扫描,因为直方图未开导致CBO错误预估了结果集
SQL>  var aa number;
SQL>  exec :aa :=77;

PL/SQL procedure successfully completed.

SQL> select status from tb1 where object_id=:aa;

STATUS
-------
VALID

SQL> @all

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID  2hn00xzrv5t77, child number 6
-------------------------------------
select status from tb1 where object_id=:aa

Plan hash value: 3226679318

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |   290 (100)|          |
|*  1 |  TABLE ACCESS FULL| TB1  | 36267 |   283K|   290   (1)| 00:00:04 |

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
--------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$1 / TB1@SEL$1

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

   1 - filter("OBJECT_ID"=:AA)

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------

Column Projection Information (identified by operation id):
-----------------------------------------------------------

   1 - "STATUS"[VARCHAR2,7]


28 rows selected.

--手工构造正确的执行计划,并记录下sql_id和plan_hash_value

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  4jqbhqxt090yp, child number 0
-------------------------------------
select /*+ index(tb1(object_id)) */status from tb1 where object_id=:aa

Plan hash value: 455808360

-------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name              | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                   |       |       |   589 (100)|          |
|   1 |  TABLE ACCESS BY INDEX ROWID| TB1               | 36267 |   283K|   589   (1)| 00:00:08 |
|*  2 |   INDEX RANGE SCAN          | IDX_TB1_OBJECT_ID | 36267 |       |    71   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$1 / TB1@SEL$1
   2 - SEL$1 / TB1@SEL$1


--关键的执行步骤
--先导入全表扫的计划到baseline
DECLARE
k1 pls_integer;
begin
k1 := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE (
sql_id=>'2hn00xzrv5t77',
plan_hash_value=>3226679318,
fixed=>'NO',
enabled=>'YES');
end;
/

--手工构造执行计划,将前面运行的手工构造的SQL计划进行替换绑定
SQL>   show user
USER is "AIKI"
SQL> declare
  2  k1 pls_integer;
  3  begin
  4  k1:=DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE (
  5  sql_id=>'4jqbhqxt090yp',
  6  plan_hash_value=>455808360,
  7  sql_handle=>'SQL_e1f977ef3e9546c4',--原来不正确BASELINE
  8  fixed=>'NO',
  9  enabled=>'YES');
 10  end;
 11  /

PL/SQL procedure successfully completed.

--修改fix属性(也可以采用将没用的baseline,将其drop掉)
SQL> 
SQL>  declare
  2  k1 pls_integer;
  3  begin
  4  k1:=DBMS_SPM.ALTER_SQL_PLAN_BASELINE (
  5  sql_handle=>'SQL_e1f977ef3e9546c4',
  6  plan_name=>'SQL_PLAN_f3ybrxwz9ajq4be29918e',
  7  attribute_name=>'FIXED',
  8  attribute_value=>'YES');
  9  end;
 10  /

PL/SQL procedure successfully completed.


--检查生效
SQL> show user
USER is "AIKI"
SQL>  var aa number;
SQL>  exec :aa :=77;

PL/SQL procedure successfully completed.

SQL> select status from tb1 where object_id=:aa;

STATUS
-------
VALID

SQL>  var aa number;
SQL>  exec :aa :=77;

PL/SQL procedure successfully completed.

SQL> select status from tb1 where object_id=:aa;

STATUS
-------
VALID

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  2hn00xzrv5t77, child number 6
-------------------------------------
select status from tb1 where object_id=:aa

Plan hash value: 455808360

-------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name              | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                   |       |       |   589 (100)|          |
|   1 |  TABLE ACCESS BY INDEX ROWID| TB1               | 36267 |   283K|   589   (1)| 00:00:08 |
|*  2 |   INDEX RANGE SCAN          | IDX_TB1_OBJECT_ID | 36267 |       |    71   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$1 / TB1@SEL$1
   2 - SEL$1 / TB1@SEL$1

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

   2 - access("OBJECT_ID"=:AA)

Column Projection Information (identified by operation id):
-----------------------------------------------------------

   1 - "STATUS"[VARCHAR2,7]
   2 - "TB1".ROWID[ROWID,10]

Note
-----
   - SQL plan baseline SQL_PLAN_f3ybrxwz9ajq4be29918e used for this statement


35 rows selected.

--可以看到人工构造的执行计划已经生效了;


<think>好的,用户想知道如何通过执行计划来诊断和解决Oracle 11g中SQL查询的性能问题,特别是分析执行计划以定位性能瓶颈的最佳方法。我需要先回忆一下Oracle中执行计划的相关知识,然后结合用户提供的引用内容来组织回答。 首先,用户提到了几个引用,其中引用[1]提到了set auto trace和SQL_TRACE,这两个工具可以用来获取执行计划和实际执行情况。引用[2]提到了索引优化的重要性,而引用[3]则涉及SQL重演技术。我需要将这些内容整合到回答中。 第一步应该是获取执行计划。Oracle中常用的方法有EXPLAIN PLAN和AUTOTRACE,以及SQL*Plus中的DBMS_XPLAN。这里需要详细说明每个方法的使用步骤,比如使用SET AUTOTRACE ON来获取执行计划,或者用DBMS_XPLAN.DISPLAY来显示。同时要提到SQL_TRACE和TKPROF,因为它们能提供实际的执行数据,而不仅仅是优化器的估算。 接下来是分析执行计划的步骤。要检查执行计划的顺序,比如从最内层开始,查看是否有全表扫描,这可能是缺少索引的迹象。引用[2]里提到索引优化的重要性,所以这里需要强调检查全表扫描并考虑添加索引。同时,要注意连接操作的类型,比如哈希连接或嵌套循环,哪种更高效取决于数据量。还要关注成本估算和实际资源消耗的差异,因为优化器的估算可能不准确,这时候可能需要统计信息更新。 然后是结合其他诊断工具,比如引用[1]中的SQL_TRACE和TKPROF,可以分析实际的执行情况,包括CPU时间、物理读等。另外,引用[3]的SQL重演技术可以用来比较不同执行计划的效果,帮助确定最优方案。 优化策略部分,需要提到索引优化(引用[2])、SQL重写(引用[2]里的查询重写)、统计信息维护以及执行计划绑定(引用[3]的SQL Plan Baseline)。这些内容要具体说明,比如如何添加复合索引,如何重写查询避免复杂的子查询,或者使用绑定变量等。 最后,给出一个具体案例,比如用户遇到全表扫描,通过添加索引优化,并比较优化前后的执行计划变化。这样可以让用户更直观地理解应用方法。 在组织回答时,需要确保结构清晰,分步骤说明,每个步骤都结合引用内容,并在适当位置添加引用标识。同时,要使用中文,避免使用任何Markdown格式,行内数学公式用$...$,独立公式用$$...$$,不过这个问题里可能公式较少,但需要注意。最后生成相关问题,围绕执行计划分析的其他方面提问,比如索引优化方法或统计信息更新策略,以及SQL重演技术的应用场景。</think>### 通过执行计划诊断和解决Oracle 11g SQL性能问题的步骤 #### 1. **获取执行计划** - **使用`EXPLAIN PLAN`生成预估执行计划** 通过`EXPLAIN PLAN FOR`命令生成优化器预测的执行路径,并通过`DBMS_XPLAN.DISPLAY`查看结果。示例: ```sql EXPLAIN PLAN FOR SELECT * FROM employees WHERE department_id = 10; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY); ``` 这会输出执行计划的步骤、操作类型(如全表扫描、索引扫描)及估算成本[^1]。 - **通过`AUTOTRACE`获取实际执行计划** 在SQL*Plus中启用`SET AUTOTRACE ON`,执行SQL后会自动显示执行计划和统计信息(如逻辑读、物理读)。适用于快速验证优化效果[^1]。 - **使用`SQL_TRACE`和`TKPROF`分析实际执行** 开启会话级跟踪: ```sql ALTER SESSION SET SQL_TRACE = TRUE; -- 执行目标SQL ALTER SESSION SET SQL_TRACE = FALSE; ``` 通过`TKPROF`工具解析跟踪文件,生成包含执行时间、I/O消耗和等待事件的详细报告。 #### 2. **分析执行计划的关键点** - **检查全表扫描(FULL TABLE SCAN)** 若大表出现全表扫描,可能因缺失索引或索引失效。例如,`WHERE department_id = 10`若未在`department_id`列建索引,会导致全表扫描。添加索引可优化: ```sql CREATE INDEX idx_dept ON employees(department_id); ``` 但需权衡索引维护成本[^2]。 - **评估连接操作类型** - **嵌套循环连接(NESTED LOOPS)**:适合小数据集驱动。 - **哈希连接(HASH JOIN)**:适合无索引的大表关联。 - **排序合并连接(SORT MERGE JOIN)**:需预先排序数据。 若连接方式不符合数据分布(如大表使用嵌套循环),需检查表统计信息或调整查询结构。 - **识别高成本操作** 关注执行计划中成本占比最高的步骤(如`COST=100`)。例如,若排序操作(`SORT ORDER BY`)成本高,可尝试减少排序数据量或利用索引避免排序。 #### 3. **结合统计信息验证准确性** - **检查统计信息时效性** 执行计划依赖统计信息,过时的统计信息会导致优化器选择低效路径。更新统计信息: ```sql EXEC DBMS_STATS.GATHER_TABLE_STATS('HR', 'EMPLOYEES'); ``` - **比较预估行数与实际行数** 若执行计划的`Rows`列(预估行数)与实际返回行数差异大,可能统计信息不准确。例如,预估返回10行但实际返回1000行,需重新收集统计信息。 #### 4. **优化策略实施** - **索引优化** - 为高频查询条件列添加复合索引,如`CREATE INDEX idx_name_dept ON employees(last_name, department_id)`。 - 避免过度索引,删除未使用的索引以减少写操作开销。 - **SQL重写** - 将子查询改写为连接: ```sql -- 原查询 SELECT * FROM employees WHERE department_id IN (SELECT department_id FROM departments WHERE location_id = 1700); -- 改写为 SELECT e.* FROM employees e JOIN departments d ON e.department_id = d.department_id WHERE d.location_id = 1700; ``` - 使用`WITH`子句预计算公共表达式。 - **绑定执行计划(SQL Plan Baseline)** 对于已验证高效的执行计划,使用SQL Plan Baseline固定: ```sql DECLARE v_plan PLS_INTEGER; BEGIN v_plan := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(sql_id => 'abc123'); END; ``` 这能防止优化器因统计信息变化选择低效计划[^3]。 #### 5. **案例演示** **问题场景**:查询`SELECT * FROM orders WHERE order_date BETWEEN '2023-01-01' AND '2023-12-31'`执行缓慢。 **诊断**:执行计划显示全表扫描`ORDERS`表,`COST=1500`。 **优化**: - 在`order_date`列创建索引: ```sql CREATE INDEX idx_order_date ON orders(order_date); ``` - 更新统计信息后,执行计划变为索引范围扫描,`COST=200`。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值