当前位置: 首页 > news >正文

Oracle 23AI 稳定执行计划:SQL Profile

#Oracle 23AI #稳定执行计划 #SQL Profile

引言

在 Oracle 数据库里,我们盼着所有执行的 SQL,CBO 都能给出正确执行计划,可实际常不如意。像目标 SQL 涉及对象统计信息不准,或者 CBO 自身成本计算公式有缺陷,都会让 CBO 产出低效甚至错误的执行计划 。

尤其糟心的是,原本正确的初始执行计划,可能因统计信息变更等,让 CBO 重新生成错误计划,这会使 SQL 执行时间大幅增加,之前跑得好好的 SQL,突然就慢到没法接受,而这类执行效率骤降,往往就是执行计划改变导致的。

所以,我们特别希望 Oracle 数据库里的 SQL,执行计划能一直正确、稳定,这样业务运行才靠谱。但在 Oracle 11g 的 SPM(SQL Plan Management )出现前,很难实现。要是已经出现执行计划变更、CBO 产出错误计划的情况,就得想办法纠正。

常规做法是重新收集统计信息,或者修改目标 SQL(比如加 Hint ),可有时重新收集统计信息没用,还存在没法修改目标 SQL 文本的情况(像系统是第三方开发的,改不了源码;或者 SQL 是前台框架动态生成的 ),这时候就需要其他手段(像 Oracle 10g/11g 及后续版本里的 SQL Profile、SPM )来解决,以此保证执行计划稳定,让 SQL 高效运行 。 简单说,稳定执行计划,就是为了避免 SQL 执行效率突变,保障数据库业务高效、稳定跑起来,遇到计划变更问题时,也得有办法应对。

本文将重点介绍在ORACLE 23AI环境中,SQL Profile是如何稳定执行的


注意

本文近9000字,请读者耐心食用!另外需要coe_load_sql_profile_v2.sql脚本的读者,三连后添加的微信号anya18420免费获取。


一、什么是SQL Profile

SQL Profile 是 优化器辅助统计信息存储对象,通过 修正 SQL 执行时的成本计算、选择性估计,影响执行计划选择。其本质是向优化器注入 人工调整的统计信息(如直方图修正、访问路径权重),使相同 SQL 文本(严格匹配)始终按预期逻辑生成执行计划,避免因统计信息、数据分布或优化器版本变化导致的计划波动。

二、为什么要使用SQL Profile

2.1. 解决优化器信息缺失问题

当统计信息过时、数据分布倾斜(如直方图缺失)或绑定变量窥探(Bind Peeking)导致优化器对 基数(Cardinality)、选择性(Selectivity) 估计错误时,SQL Profile 通过 注入辅助统计信息(如修正因子、动态采样结果),修正优化器的成本计算,使执行计划选择更准确,避免因信息不足生成低效计划。

2.2. 稳定执行计划,避免波动

  • 自动场景:由 SQL Tuning Advisor 生成的 Profile,动态适配数据变化(如表增长、索引变更),持续优化计划选择(而非固定单一计划),确保长期性能稳定。
  • 手工场景:通过绑定 hint 或自定义统计(如固定 join 顺序、索引优先级),强制优化器按预设逻辑生成计划,解决因绑定变量、统计信息更新导致的计划切换问题(如 “绑定变量敏感” SQL 的计划波动)。

2.3. 无代码侵入,运维友好

  • 无需修改 SQL 文本:适用于第三方系统、遗留代码中无法改动的 SQL(如 SELECT * FROM T 类低效语句),通过 Profile 注入优化逻辑(如隐式 hint 转换),直接在数据库层优化,降低应用侧改造成本。
  • 会话级控制:通过 CATEGORY 属性(如 DEV 环境测试、PROD 环境生效),灵活管理 Profile 作用范围,避免影响全库,提升运维安全性。

2.4. 替代旧特性,功能更优

  • 对比 Outline:Profile 支持 自动生成(Tuning Advisor 一键优化)、更灵活的统计信息调整(如动态采样修正)、跨 SQL 文本匹配(force_match 处理字面值变化),管理更便捷,适用场景更广(如 DML 含 SELECT 子句、复杂查询)。
  • 对比 Hint:Profile 优先级高于 SQL 中的 hint(可覆盖低效 hint,如强制索引访问即使代码含 NO_INDEX),且无需在 SQL 中硬编码 hint,便于统一管理(如批量优化相似 SQL)。

2.5. 应对特定调优需求

  • 强制最优路径:在已知最优执行计划(如索引扫描优于全表扫描)但优化器未选择时,通过手工 Profile 固定访问路径,绕过优化器的 “错误决策”。
  • 跨环境迁移:导出 / 导入 Profile,将测试环境验证的优化计划快速应用到生产,减少重复调优成本。

三、SQL Profile分类与优缺点

3.1 Auto SQL Profile(基于 SQL Tuning Advisor)

Automatic 类型的 SQL Profile,本质是针对目标 SQL 的额外调整信息,这些信息存在数据字典里。当有了它,Oracle 生成执行计划时,会依据这些信息,结合目标 SQL 涉及的统计信息等做调整,能在一定程度上避免生成错误执行计划。不用担心其准确性,Oracle 会用类似动态采样技术的手段,保证这些额外调整信息相对靠谱。

打个比方,表 A 原始统计信息记着只有 100 条数据,实际却有 100 万条。要是 CBO 只依据原始统计信息生成执行计划,大概率就错了;但给表 A 用 Automatic 类型的 SQL Profile,Oracle 在数据字典里记上目标 SQL 的 “SCALE_ROWS=10000”(意思是表 A 实际记录数是原始统计信息的 10000 倍 ),这样 CBO 再生成执行计划时,就会认为表 A 有 100*10000 = 100 万条记录,生成的计划就可能是对的。

(一) 原理:

  • 分析阶段:SQL Tuning Advisor 对目标 SQL 进行 执行计划分析、统计信息校验、访问路径模拟(如检查索引缺失、全表扫描成本异常)。
  • 生成建议:根据分析结果,自动生成 优化后的统计信息配置(如修正列选择性、调整 join 成本因子),封装为 SQL Profile。
  • 生效逻辑:Profile 存储于数据字典,优化器解析 SQL 时,优先使用 Profile 中的统计信息计算执行计划成本,覆盖默认统计信息和绑定变量窥探的影响。

(二) 适用场景:

  • 系统级 SQL 调优(如 AWR 中高频低效 SQL 自动优化),减少人工干预,自动稳定因统计信息过时或数据倾斜导致的计划波动。

(三) 优点:

  1. 自动化程度高:
  • 无需人工分析执行计划或编写 Hint,工具自动完成统计信息收集、成本模型修正及 Profile 生成,降低调优门槛。
  • 适用于批量优化大量 SQL(如通过 DBMS_SQLTUNE 包批量生成),提升运维效率。
2. 动态适应性强:
  • 基于实时统计信息与动态采样(Dynamic Sampling)生成,可随数据分布变化(如新增索引、表数据增长)自动调整优化逻辑,避免手工配置的 “静态固化” 问题。
  • 支持 AUTO 类别,默认在全库生效,无需手动指定作用范围。
3. 风险较低:
  • 生成的 Profile 会经过 Oracle 优化器内部校验(如成本对比),默认不会强制极端路径(如禁用所有索引),减少因人工误判导致的性能倒退。

(四) 缺点:

  1. 优化力度有限:
  • 受限于优化器自身规则(如不突破 “启发式优化” 边界),无法实现手工干预的 “激进优化”(如强制嵌套循环连接替代哈希连接)。
  • 对复杂绑定变量敏感 SQL(如不同变量值对应差异极大的执行路径),可能无法生成普适性强的 Profile。
2. 环境依赖性高:
  • 生成的 Profile 高度依赖当前环境的统计信息与参数设置(如 OPTIMIZER_MODE),跨环境(如从测试到生产)迁移时可能因数据量或索引差异失效。
  • 若自动采样数据量不足(如大表仅采样 5%),可能导致基数估计仍存在偏差。

3. 管理透明度低:

  • 自动生成的统计信息修正逻辑(如隐藏的 SQLPROF$ 表数据)对 DBA 不可见,难以追溯优化依据,调优问题定位困难。

3.2、手工SQL Profile

手工 SQL Profile 本质是 Hint 组合,源于执行计划 Outline Data 的 Hint 集合。它无需修改 SQL 文本,可调整执行计划,且能稳定目标 SQL 的执行计划(此为自动类型所无)。通过手动注入特定 Hint(如固定连接顺序、指定索引访问等),精准控制优化器行为,适用于已知最优路径、复杂绑定变量或统计信息缺失场景。需 DBA 深入理解优化器原理,编写或导入 Hint 组合,虽技术门槛高、维护成本大,但可绕过优化器 “错误决策”,实现执行计划的强管控,是解决特定调优问题的关键手段。

(一) 原理:

1. 生成原始 Profile 脚本(A)
  • 对目标 SQL(如 select /*+ no_index(t1 idx_t1) */ * from t1 where n=3)运行脚本,生成用于创建其 Manual Profile 的脚本 A(包含原始 Outline Data 的 Hint 组合)。

2. 改写 SQL 并生成目标 Profile 脚本(B)

  • 修改目标 SQL,添加所需 Hint(如强制走索引 /*+ index(t1 idx_t1) */),使执行计划符合预期(如索引扫描代替全表扫描)。
  • 对改写后的 SQL 运行脚本,生成脚本 B(包含新 Hint 组合的 Outline Data)。

3. 替换 Hint 组合

  • 提取脚本 B 中 Outline Data 部分的 Hint 集合,替换脚本 A 中对应的 Hint 内容(保留其他元数据,仅修改执行计划相关的 Hint)。

4. 创建最终 Profile

  • 执行修改后的脚本 A,生成针对 原始目标 SQL 的 Manual Profile。此时,原始 SQL 会加载新 Profile 中的 Hint 组合,执行计划被调整为脚本 B 中定义的优化路径(如强制索引扫描)。

(二) 适用场景:

  • 已知执行计划波动场景(如绑定变量导致的计划切换,手工固定最优路径);
  • 第三方 SQL 无法修改代码时(通过 Profile 注入 hint 逻辑,稳定执行计划,无需改动 SQL 文本)。

(三) 优点:

1. 精准控制执行计划:

  • 可强制指定优化器行为(如固定 join 顺序、启用特定索引、设置 FACTOR 修正基数),绕过优化器的 “错误决策”,适用于已知最优路径的场景(如历史经验验证的计划)。
  • 支持直接嵌入 Hint(通过 hint 参数),优先级高于 SQL 文本中的硬编码 Hint,确保优化逻辑生效。

2. 灵活适配特殊场景:

  • 处理自动模式无法覆盖的边缘情况,如:
  • 数据字典统计信息缺失但业务已知分布(如罕见高选择性过滤条件);
  • 需要跨 SQL 文本匹配(通过 FORCE_MATCH 参数,将字面值差异的 SQL 视为同一模板)。
  • 可按会话、用户或环境(通过 CATEGORY 属性)精细控制 Profile 生效范围,避免全局影响。

3. 可维护性强:

  • 手动配置的统计信息修正或 Hint 逻辑清晰可见,便于后续调整与问题排查(如通过 DBMS_SQLPROF 包查看 Profile 内容)。

(四) 缺点:

1. 技术门槛高:

  • 需要深入理解优化器成本模型(如基数估计公式、连接算法成本计算),否则可能因参数设置错误导致计划更差(如错误设置 SELECTIVITY 引发笛卡尔积)。
  • 手工编写 EXTENDED 类型 Profile(含列组统计信息)时,需精准分析谓词组合,容易遗漏关键列。

 2. 维护成本高:

  • 当表结构变更(如新增索引、列数据类型修改)或统计信息更新后,手工 Profile 可能过时,需人工重新评估有效性(如通过 EXPLAIN PLAN 对比新旧计划)。
  • 大量手工 Profile 可能导致数据字典膨胀(SQLPROF$ 表记录增多),影响数据库元数据性能。

3. 潜在性能风险:

  • 强制固定计划可能抑制优化器的自适应能力(如当索引失效时仍强制索引扫描,导致全表扫描被禁用),引发 “过度优化” 问题。
  • 错误的 FORCE_MATCH 配置可能导致 Profile 匹配到非预期 SQL(如将 WHERE ID=1 的 Profile 应用于 WHERE NAME='ABC' 的语句),引发执行计划错乱。

3.3、两类对比

维度

自动生成(Tuning Advisor)

手工创建(hint / 自定义统计)

依赖工具

依赖 Oracle 自动调优框架(无需人工编写 hint)

需人工编写 hint 或调整统计信息(对优化器逻辑熟悉度要求高)

适用场景

通用 SQL 性能优化,系统自动稳定计划

特定场景(如已知计划缺陷,手工强制路径)

灵活性

自动适配数据变化(动态优化统计信息)

静态固定执行逻辑(适合完全已知的最优计划场景)

四、实验场景

4.1 Automatic 类型的SQL Profile

下面看个实例,在不改目标 SQL 文本的情况下,用 Automatic 类型的 SQL Profile 调整执行计划 。

以下环境在PDB下进行,为了方便,使用的SYS用户。

(一) 实验环境准备

sqlplus sys/oracle@db1/freepdb1:1521 as sysdba--创建表
SQL> create table t1 as select * from dba_objects;Table created.--创建索引
SQL> create index idx_t1_id on t1(object_id);Index created.--收集统计信息
begindbms_stats.gather_table_stats(ownname => 'SYS' ,tabname =>'T1',method_opt => 'for all columns size 1', estimate_percent => 30,no_invalidate => false,degree => 2, cascade => true); 
end;
/

(二) 模拟不好的执行计划

--通过full hint走全表扫描
set autot trace
select /*+ full(t1)*/ * from t1 where object_id=87;--执行计划如下
Execution Plan
----------------------------------------------------------
Plan hash value: 3617692013--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |   149 |   421   (1)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T1   |     1 |   149 |   421   (1)| 00:00:01 |
--------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------1 - filter("OBJECT_ID"=87)Statistics
----------------------------------------------------------66  recursive calls0  db block gets1667  consistent gets0  physical reads0  redo size3371  bytes sent via SQL*Net to client108  bytes received via SQL*Net from client2  SQL*Net roundtrips to/from client4  sorts (memory)0  sorts (disk)1  rows processed
SQL> 

从上面的结果可以看出,目标 SQL 对表 T1 执行全表扫描(Table Access Full),而正确计划应为索引 IDX_T1 的范围扫描(Index Range Scan),存在优化器决策错误(如统计信息缺失、成本计算偏差)。

(三) 创建自动任务

针对 T1 表全表扫描的错误计划,利用 SQL Tuning Advisor 创建自动调优任务,生成并应用 Automatic SQL Profile

declaremy_task_name varchar2(30);my_sqltext CLOB;
beginmy_sqltext :='select /*+ full(t1)*/ * from t1 where object_id=87';my_task_name := dbms_sqltune.create_tuning_task(sql_text => my_sqltext,user_name =>'SYS' ,scope =>'COMPREHENSIVE' ,time_limit=>60,task_name =>'T1_TUNING_TASK2' ,description =>'this is a test task' );
end;
/PL/SQL procedure successfully completed.SQL>

(四)  执行自动任务

begindbms_sqltune.execute_tuning_task(task_name => 'T1_TUNING_TASK2');
end;
/PL/SQL procedure successfully completed.SQL> 

(五) 查看任务分析结果

set long 9990
set pages 1000
set linesize 1000
set longchunksize 1000
select dbms_sqltune.report_tuning_task(task_name => 'T1_TUNING_TASK2') from dual;
SQL>
DBMS_SQLTUNE.REPORT_TUNING_TASK(TASK_NAME=>'T1_TUNING_TASK1')
GENERAL INFORMATION SECTION
-------------------------------------------------------------------------------
Tuning Task Name   : T1_TUNING_TASK1
Tuning Task Owner  : SYS
Workload Type      : Single SQL Statement
Scope              : COMPREHENSIVE
Time Limit(seconds): 60
Completion Status  : COMPLETED
Started at         : 06/04/2025 10:34:01
Completed at       : 06/04/2025 10:34:04-------------------------------------------------------------------------------
Schema Name   : SYS
Container Name: FREEPDB1
SQL ID        : 8fm0dn0gj3n9a
SQL Text      : select /*+ full(t1)*/ * from t1 where object_id=87-------------------------------------------------------------------------------
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: 99.8%)------------------------------------------ Consider accepting the recommended SQL profile.BEGINdbms_sqltune.accept_sql_profile(task_name => 'T1_TUNING_TASK1',task_owner => 'SYS',replace => TRUE);END;/Validation results------------------The SQL profile was tested by executing both its plan and the original planand measuring their respective execution statistics. A plan may have beenonly partially executed if the other could be run to completion in less time.Original Plan  With SQL Profile  % Improved-------------  ----------------  ----------Completion Status:            COMPLETE          COMPLETEElapsed Time (s):              .00566           .000024      99.57 %CPU Time (s):                 .005297           .000024      99.54 %User I/O Time (s):                  0                 0Buffer Gets:                     1549                 3       99.8 %Physical Read Requests:             0                 0Physical Write Requests:            0                 0Physical Read Bytes:                0                 0Physical Write Bytes:               0                 0Rows Processed:                     1                 1Fetches:                            1                 1Executions:                         1                 1Notes-----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: 3617692013--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |   149 |   421   (1)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T1   |     1 |   149 |   421   (1)| 00:00:01 |
--------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------1 - filter("OBJECT_ID"=87)Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 3 (U - Unused (3))
---------------------------------------------------------------------------0 -  STATEMENTU -  IGNORE_OPTIM_EMBEDDED_HINTS / hint overridden by another in parent query blockU -  OPTIMIZER_FEATURES_ENABLE(default) / hint overridden by another in parent query block1 -  SEL$1 / "T1"@"SEL$1"U -  full(t1) / rejected by IGNORE_OPTIM_EMBEDDED_HINTS2- Using SQL Profile
--------------------
Plan hash value: 2067802950-------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |           |     1 |   149 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| T1        |     1 |   149 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN                  | IDX_T1_ID |     1 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------2 - access("OBJECT_ID"=87)Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1 (U - Unused (1))
---------------------------------------------------------------------------1 -  SEL$1 / "T1"@"SEL$1"U -  full(t1) / rejected by IGNORE_OPTIM_EMBEDDED_HINTS-------------------------------------------------------------------------------

结果表明,该 SQL 调优任务(T1_TUNING_TASK1)针对 SELECT /*+ full(t1)*/ * FROM t1 WHERE object_id=87 语句(SQL ID: 8fm0dn0gj3n9a)进行分析,发现优化器因 FULL(T1) Hint 使用了全表扫描(成本 421),而实际存在更优执行计划(成本 2)。建议接受自动生成的 SQL Profile,其通过索引 IDX_T1_ID 执行范围扫描后按 ROWID 批量访问数据,将 Buffer Gets 从 1549 降至 3,执行时间从 0.00566 秒降至 0.000024 秒,性能提升 99.57%。验证测试显示,新计划在 10 次执行中均显著优于原计划,且原 Hint 被自动忽略,表明统计信息修正有效覆盖了手动 Hint 的影响。

(六) 接受分析结果

上面的auto sql pfile正是我们要的,按上面的提示接受它即可

BEGINdbms_sqltune.accept_sql_profile(task_name => 'T1_TUNING_TASK1',task_owner => 'SYS',replace => TRUE);END;/

(七) 检查结果

针对实施的变更,检查目标SQL执行计划是否改变

set autot trace 
select /*+ full(t1)*/ * from t1 where object_id=87;Execution Plan
----------------------------------------------------------
Plan hash value: 2067802950-------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |           |     1 |   149 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| T1        |     1 |   149 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN                  | IDX_T1_ID |     1 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------2 - access("OBJECT_ID"=87)Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1 (U - Unused (1))
---------------------------------------------------------------------------1 -  SEL$1 / "T1"@"SEL$1"U -  full(t1) / rejected by IGNORE_OPTIM_EMBEDDED_HINTSNote
------ SQL profile "SYS_SQLPROF_019738d0252b0000" used for this statementStatistics
----------------------------------------------------------37  recursive calls0  db block gets17  consistent gets1  physical reads0  redo size3371  bytes sent via SQL*Net to client108  bytes received via SQL*Net from client2  SQL*Net roundtrips to/from client1  sorts (memory)0  sorts (disk)1  rows processed
SQL> 

通过接受AUTO SQL PROFILE之后,该 SQL 执行虽包含 /*+ FULL(T1) */ Hint,但实际执行计划通过索引 IDX_T1_ID 扫描(Plan Hash: 2067802950),且执行信息显示使用了 SQL Profile(SYS_SQLPROF_019738d0252b0000),表明 Profile 优先级高于手动 Hint;统计数据显示逻辑读从 1549 降至 17、物理读 1 次,性能提升显著,验证了 SQL Tuning Advisor 生成的 Profile 有效修正了优化器决策,将低效的全表扫描转为高效的索引访问路径。

如果换一个传参,结果会如何?

 我们将where object_id=87 改为where object_id=187

set autot trace 
select /*+ full(t1)*/ * from t1 where object_id=187;Execution Plan
----------------------------------------------------------
Plan hash value: 3617692013--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |   149 |   421   (1)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T1   |     1 |   149 |   421   (1)| 00:00:01 |
--------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------1 - filter("OBJECT_ID"=187)Statistics
----------------------------------------------------------5  recursive calls0  db block gets1564  consistent gets0  physical reads0  redo size3374  bytes sent via SQL*Net to client108  bytes received via SQL*Net from client2  SQL*Net roundtrips to/from client0  sorts (memory)0  sorts (disk)1  rows processedSQL> 

执行计划又走全表扫描了,没有走我们预期的索引范围扫描,什么原因?

通过查看官方文档,DBMS_SQLTUNE.ACCEPT_SQL_PROFILE 的 FORCE_MATCH 参数默认值为 FALSE,即仅在 SQL 文本完全匹配时应用 SQL Profile,若目标 SQL 文本有改动,原有 SQL Profile 就失效。

DBMS_SQLTUNE.ACCEPT_SQL_PROFILE (task_name IN VARCHAR2,object_id IN NUMBER := NULL,name IN VARCHAR2 := NULL,description IN VARCHAR2 := NULL,category IN VARCHAR2 := NULL);task_owner IN VARCHAR2 := NULL,replace IN BOOLEAN := FALSE,force_match IN BOOLEAN := FALSE,--默认值为FALSEprofile_type IN VARCHAR2 := REGULAR_PROFILE);force_match
如果为 TRUE,这会使 SQL 配置文件针对所有在将所有文字值(literal values)归一化为绑定变量(bind variables)后文本相同的 SQL 语句。(注意:如果 SQL 语句中同时使用文字值和绑定值,不会发生绑定转换。)这类似于游标共享(cursor_sharing)参数的 FORCE 选项所采用的匹配算法。
如果为 FALSE,文字值不会被转换。这类似于游标共享参数的 EXACT 选项所采用的匹配算法。

(八) 调整FORCE_MATCH 参数

在执行计划优化过程中,我们发现当 SQL 查询的 WHERE 子句参数从 "object_id=87" 调整为 "object_id=187" 时,原有的 SQL Profile 会失效,导致查询回归到对表 T1 的全表扫描模式。这表明 Oracle 的 SQL Profile 默认情况下对字面量参数敏感,无法适应参数值的变化。

若需要使 SQL Profile 对参数化查询保持持续有效,可以在使DBMS_SQLTUNE.ACCEPT_SQL_PROFILE 过程时,将 FORCE_MATCH 参数设置为 TRUE。该参数启用了 Oracle 的 "绑定变量窥探"(Bind Peeking)机制的优化匹配模式,允许 SQL Profile 忽略 WHERE 子句中的具体参数值差异,将其视为绑定变量处理。

以下是修改后的存储过程调用示例:

-- 启用强制匹配模式,使SQL Profile适配参数变化
EXECUTE dbms_sqltune.accept_sql_profile(task_name   => 'T1_TUNING_TASK1', task_owner  => 'SYS', replace     => TRUE,  force_match => TRUE);
PL/SQL procedure successfully completed.
SQL> 

执行上述命令后,SQL Profile 将能够匹配所有具有相同结构的查询,无论 WHERE 子句中的参数值如何变化,从而保持执行计划的稳定性。

(九) 查看适配结果

再次查看select /*+ full(t1)*/ * from t1 where object_id=187 的执行计划。

SQL> set autot trace 
select /*+ full(t1)*/ * from t1 where object_id=187;
SQL> Execution Plan
----------------------------------------------------------
Plan hash value: 2067802950-------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |           |     1 |   149 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| T1        |     1 |   149 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN                  | IDX_T1_ID |     1 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------2 - access("OBJECT_ID"=187)Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1 (U - Unused (1))
---------------------------------------------------------------------------1 -  SEL$1 / "T1"@"SEL$1"U -  full(t1) / rejected by IGNORE_OPTIM_EMBEDDED_HINTSNote
------ SQL profile "SYS_SQLPROF_01973906c6da0001" used for this statementStatistics
----------------------------------------------------------35  recursive calls0  db block gets17  consistent gets1  physical reads0  redo size3374  bytes sent via SQL*Net to client108  bytes received via SQL*Net from client2  SQL*Net roundtrips to/from client1  sorts (memory)0  sorts (disk)1  rows processed

从上述内容可知,当将 FORCE_MATCH 的值设为 TRUE 并重新执 DBMS_SQLTUNE.ACCEPT_SQL_PROFILE 后,新生成的 SQL Profile(名称为 SYS_SQLPROF_01339cce6e980001)成功生效,这使得目标 SQL 的执行计划发生了改变,从原先对表 T1 的全表扫描转变为对索引 IDX_T1 的索引范围扫描,优化了查询性能。

有兴趣的读者可以换其它传参进行多次尝试验证。

(十) AUTO SQL PROFILE的缺点

接下来通过模拟数据分布不均衡的场景,验证其缺点。

  • 环境准备
SQL> select count(1) from t1;COUNT(1)
----------595466SQL> select count(1) from t1 where object_id=187;COUNT(1)
----------524288SQL> select count(1) from t1 where object_id=87;COUNT(1)
----------1
  • 收集带直方图的统计信息
SQL> begindbms_stats.gather_table_stats(ownname => 'SYS' ,tabname =>'T1',method_opt => 'for all columns size auto', estimate_percent => 30,no_invalidate => false,degree => 2, cascade => true); end;/
  • 查看条件object_id=187的执行计划
 select /*+ full(t1)*/ * from t1 where object_id=187;524288 rows selected.Execution Plan
----------------------------------------------------------
Plan hash value: 3617692013--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |   522K|    52M|  2521   (1)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T1   |   522K|    52M|  2521   (1)| 00:00:01 |
--------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------1 - filter("OBJECT_ID"=187)Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1 (U - Unused (1))
---------------------------------------------------------------------------1 -  SEL$1 / "T1"@"SEL$1"U -  full(t1) / rejected by IGNORE_OPTIM_EMBEDDED_HINTSNote
------ SQL profile "SYS_SQLPROF_01973906c6da0001" used for this statement

执行计划变成了全表扫描,虽然它显示了SQL profile "SYS_SQLPROF_01973906c6da0001"被应用到SQL中。

  • 查看条件object_id=87的执行计划
 select /*+ full(t1)*/ * from t1 where object_id=87;Execution Plan
----------------------------------------------------------
Plan hash value: 2067802950-------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |           |     1 |   105 |     4   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| T1        |     1 |   105 |     4   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN                  | IDX_T1_ID |     1 |       |     3   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------2 - access("OBJECT_ID"=87)Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1 (U - Unused (1))
---------------------------------------------------------------------------1 -  SEL$1 / "T1"@"SEL$1"U -  full(t1) / rejected by IGNORE_OPTIM_EMBEDDED_HINTSNote
------ SQL profile "SYS_SQLPROF_019738d0252b0000" used for this statement

第三章节点我们提过,AUTO SQL PROFILE容易受到统计信息影响,它并不能真正完全稳定SQL的执行计划,通过上面的实验,我们得到了验证。

4.2 Manual类型的SQL Profile

下面看个实例,在不改目标 SQL 文本的情况下,用 Manual类型的 SQL Profile 调整执行计划 。

与Automatic类型的SQL Profile不同,Manual类型的SQL Profile需要通过 coe_xfr_sql_profile.sql 脚本(需要脚本可以关注公众号后添加微信助手获取),从 Shared Pool 或 AWR 存储库 中提取目标 SQL 的 Outline Data(Hint 组合),创建 Manual 类型 SQL Profile。

(一) 删除原来的profile

--确认profile名称
col name for a30
select name from dba_sql_profiles t where t.sql_text like 'select /*+ full(t1)*/ * from t1%';NAME
------------------------------
SYS_SQLPROF_01973906c6da0001--删除profile
exec dbms_sqltune.drop_sql_profile('SYS_SQLPROF_01973906c6da0001');PL/SQL procedure successfully completed.Commit complete.
SQL> 

(二) 模拟问题SQL

set autot trace 
select /*+ full(t1)*/ * from t1 where object_id=87;Execution Plan
----------------------------------------------------------
Plan hash value: 3617692013--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |   105 |  2515   (1)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T1   |     1 |   105 |  2515   (1)| 00:00:01 |
--------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------1 - filter("OBJECT_ID"=87)

select /*+ full(t1)*/ * from t1 where object_id=87 ,这个SQL由于加入了HINT,原本应该走索引的执行计划,现在走的全表扫描。

(三) 查出问题SQL的SQLID和PLAN_HASH_VALUE

select t.sql_id,t.plan_hash_value from v$sql t where t.sql_text like 'select /*+ full(t1)*/ * from t1 where object_id=87%';SQL_ID        PLAN_HASH_VALUE
------------- ---------------
8fm0dn0gj3n9a      3617692013
SQL>

(四) 修改原SQL,添加索引HINT

添加HINT /*+ index(t1,IDX_T1_ID) */生成一个我们想要的执行计划

select /*+ index(t1,IDX_T1_ID) */ * from t1 where object_id=87;
Execution Plan
----------------------------------------------------------
Plan hash value: 2067802950-------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |           |     1 |   105 |     4   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| T1        |     1 |   105 |     4   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN                  | IDX_T1_ID |     1 |       |     3   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------2 - access("OBJECT_ID"=87)SQL>

(五) 查出目标SQL的SQLID和PLAN_HASH_VALUE

select t.sql_id,t.plan_hash_value from v$sql t where t.sql_text like 'select /*+ index(t1,IDX_T1_ID) */ * from t1 where object_id=87%';SQL_ID        PLAN_HASH_VALUE
------------- ---------------
4zf1ftyfabzr7      2067802950
SQL>

(六) 调用脚本coe_load_sql_profile_v2.sql

该脚本可以实现不同SQLID之间绑定执行计划

SQL> @coe_load_sql_profile_v2.sqlParameter 1:
SQL_ID (required)Enter value for 1: 8fm0dn0gj3n9a  <--问题SQLIDPLAN_HASH_VALUE AVG_ET_SECS
--------------- -----------3617692013      60.353Parameter 2:
PLAN_HASH_VALUE (required)Enter value for 2: 2067802950  <-- 优化后的SQL PLAN_HASH_VALUEValues passed to coe_xfr_sql_profile:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SQL_ID         : "8fm0dn0gj3n9a"
PLAN_HASH_VALUE: "2067802950"SQL>BEGIN2   IF :sql_text IS NULL THEN3    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>BEGIN2   IF :other_xml IS NULL THEN3    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_8fm0dn0gj3n9a_2067802950.sql
on TARGET system in order to create a custom SQL Profile
with plan 2067802950 linked to adjusted sql_text.SQL>REM
SQL>REM $Header: 215187.1 coe_xfr_sql_profile_8fm0dn0gj3n9a_2067802950.sql 11.4.4.4 2025/06/04 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_8fm0dn0gj3n9a_2067802950.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 8fm0dn0gj3n9a based on plan hash
SQL>REM   value 2067802950.
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_8fm0dn0gj3n9a_2067802950.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_8fm0dn0gj3n9a_2067802950');
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>DECLARE2  sql_txt CLOB;3  h       SYS.SQLPROF_ATTR;4  PROCEDURE wa (p_line IN VARCHAR2) IS5  BEGIN6  DBMS_LOB.WRITEAPPEND(sql_txt, LENGTH(p_line), p_line);7  END wa;8  BEGIN9  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 /*+ full(t1)*/ * from t1 where object_id=87]');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('23.1.0')]',20  q'[DB_VERSION('23.1.0')]',21  q'[ALL_ROWS]',22  q'[OUTLINE_LEAF(@"SEL$1")]',23  q'[INDEX_RS_ASC(@"SEL$1" "T1"@"SEL$1" ("T1"."OBJECT_ID"))]',24  q'[BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$1" "T1"@"SEL$1")]',25  q'[END_OUTLINE_DATA]');26  :signature := DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE(sql_txt);27  :signaturef := DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE(sql_txt, TRUE);28  DBMS_SQLTUNE.IMPORT_SQL_PROFILE (29  sql_text    => sql_txt,30  profile     => h,31  name        => 'coe_8fm0dn0gj3n9a_2067802950',32  description => 'coe 8fm0dn0gj3n9a 2067802950 '||:signature||' '||:signaturef||'',33  category    => 'DEFAULT',34  validate    => TRUE,35  replace     => TRUE,36  force_match => FALSE /* TRUE:FORCE (match even when different literals in SQL). FALSE:EXACT (similar to CURSOR_SHARING) */ );37  DBMS_LOB.FREETEMPORARY(sql_txt);38  END;39  /PL/SQL procedure successfully completed.SQL>WHENEVER SQLERROR CONTINUE
SQL>SET ECHO OFF;SIGNATURE
---------------------13567938547063663955SIGNATUREF
---------------------902754377183247696... manual custom SQL Profile has been createdCOE_XFR_SQL_PROFILE_8fm0dn0gj3n9a_2067802950 completedCOE_XFR_SQL_PROFILE completed.

(七) 修改脚本coe_xfr_sql_profile_8fm0dn0gj3n9a_2067802950.sql内容

force_match => FALSE修改为force_match => TRUE

DBMS_SQLTUNE.IMPORT_SQL_PROFILE (
sql_text    => sql_txt,
profile     => h,
name        => 'coe_8fm0dn0gj3n9a_2067802950',
description => 'coe 8fm0dn0gj3n9a 2067802950 '||:signature||' '||:signaturef||'',
category    => 'DEFAULT',
validate    => TRUE,
replace     => TRUE,
force_match => FORCE /* TRUE:FORCE (match even when different literals in SQL). FALSE:EXACT (similar to CURSOR_SHARING) */ );
DBMS_LOB.FREETEMPORARY(sql_txt);
END;
/

(八) 执行脚本

SQL> exec dbms_sqltune.drop_sql_profile('coe_8fm0dn0gj3n9a_2067802950');PL/SQL procedure successfully completed.SQL> SQL> 
SQL> 
SQL> @coe_xfr_sql_profile_8fm0dn0gj3n9a_2067802950.sql
SQL> REM
SQL> REM $Header: 215187.1 coe_xfr_sql_profile_8fm0dn0gj3n9a_2067802950.sql 11.4.4.4 2025/06/04 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_8fm0dn0gj3n9a_2067802950.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 8fm0dn0gj3n9a based on plan hash
SQL> REM   value 2067802950.
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_8fm0dn0gj3n9a_2067802950.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_8fm0dn0gj3n9a_2067802950');
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> DECLARE2  sql_txt CLOB;3  h       SYS.SQLPROF_ATTR;4  PROCEDURE wa (p_line IN VARCHAR2) IS5  BEGIN6  DBMS_LOB.WRITEAPPEND(sql_txt, LENGTH(p_line), p_line);7  END wa;8  BEGIN9  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 /*+ full(t1)*/ * from t1 where object_id=87]');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('23.1.0')]',20  q'[DB_VERSION('23.1.0')]',21  q'[ALL_ROWS]',22  q'[OUTLINE_LEAF(@"SEL$1")]',23  q'[INDEX_RS_ASC(@"SEL$1" "T1"@"SEL$1" ("T1"."OBJECT_ID"))]',24  q'[BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$1" "T1"@"SEL$1")]',25  q'[END_OUTLINE_DATA]');26  :signature := DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE(sql_txt);27  :signaturef := DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE(sql_txt, TRUE);28  DBMS_SQLTUNE.IMPORT_SQL_PROFILE (29  sql_text    => sql_txt,30  profile     => h,31  name        => 'coe_8fm0dn0gj3n9a_2067802950',32  description => 'coe 8fm0dn0gj3n9a 2067802950 '||:signature||' '||:signaturef||'',33  category    => 'DEFAULT',34  validate    => TRUE,35  replace     => TRUE,36  force_match => TRUE /* TRUE:FORCE (match even when different literals in SQL). FALSE:EXACT (similar to CURSOR_SHARING) */ );37  DBMS_LOB.FREETEMPORARY(sql_txt);38  END;39  /PL/SQL procedure successfully completed.SQL> WHENEVER SQLERROR CONTINUE
SQL> SET ECHO OFF;SIGNATURE
---------------------13567938547063663955SIGNATUREF
---------------------902754377183247696... manual custom SQL Profile has been createdCOE_XFR_SQL_PROFILE_8fm0dn0gj3n9a_2067802950 completed

(九) 验证SQL执行计划

验证SQLselect /*+ full(t1)*/ * from t1 where object_id=87 的执行计划

SQL> select /*+ full(t1)*/ * from t1 where object_id=87;Execution Plan
----------------------------------------------------------
Plan hash value: 2067802950-------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |           |     1 |   105 |     4   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| T1        |     1 |   105 |     4   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN                  | IDX_T1_ID |     1 |       |     3   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------2 - access("OBJECT_ID"=87)Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1 (U - Unused (1))
---------------------------------------------------------------------------1 -  SEL$1 / "T1"@"SEL$1"U -  full(t1) / rejected by IGNORE_OPTIM_EMBEDDED_HINTSNote
------ SQL profile "coe_8fm0dn0gj3n9a_2067802950" used for this statement

(十) 验证SQL执行计划

验证SQLselect /*+ full(t1)*/ * from t1 where object_id=187 的执行计划

SQL> select /*+ full(t1)*/ * from t1 where object_id=187;Execution Plan
----------------------------------------------------------
Plan hash value: 2067802950-------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |           |     1 |   105 |     4   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| T1        |     1 |   105 |     4   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN                  | IDX_T1_ID |     1 |       |     3   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
---------------------------------------------------2 - access("OBJECT_ID"=87)Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1 (U - Unused (1)) Note
------ SQL profile "coe_8fm0dn0gj3n9a_2067802950" used for this statement

在Automatic SQL PROFILE实验过程中,该SQL由于返回数据量过大,走的全表扫描。但通过Manual SQL PROFILE稳定执行计划,仍然走的索引范围扫描。

五、总结

无论是 Automatic 类型还是 Manual 类型的 SQL Profile,都为稳定 SQL 执行计划提供了有效的手段。

Automatic 类型的 SQL Profile 适合在希望数据库自动进行全面优化、减少人工干预的场景下使用,它能够快速适应数据库环境的变化,为 SQL 语句提供持续的优化支持;而 Manual 类型的 SQL Profile 则在需要对特定 SQL 进行精准控制、在不能修改 SQL 文本的情况下进行优化时发挥重要作用,管理员可以根据具体需求灵活调整执行计划。在实际的数据库管理工作中,应根据具体的业务场景、数据库环境以及对性能和稳定性的要求,合理选择和运用这两种类型的 SQL Profile,以确保数据库系统始终保持高效、稳定的运行状态。


🚀 更多数据库干货,欢迎关注【安呀智数据坊】

如果你觉得这篇文章对你有帮助,欢迎点赞 👍、收藏 ⭐ 和留言 💬 交流,让我知道你还想了解哪些数据库知识!

📬 想系统学习更多数据库实战案例与技术指南?

📊 实战项目分享

📚 技术原理讲解

🧠 数据库架构思维

🛠 工具推荐与实用技巧

立即关注,持续更新中 👇

http://www.lryc.cn/news/617961.html

相关文章:

  • 训练苹果风格Emoji生成模型的技术方案
  • Docker-09.Docker基础-Dockerfile语法
  • 数据上云有什么好处?企业数据如何上云?
  • Flutter Provider 状态管理全面解析与实战应用:从入门到精通
  • priority_queue(优先级队列)和仿函数
  • 关于linux系统编程2——IO编程
  • 内网依赖管理新思路:Nexus与CPolar的协同实践
  • redis常见的性能问题
  • Redis 数据倾斜
  • day072-代码检查工具-Sonar与maven私服-Nexus
  • Qt 5.14.2安装教程
  • 基于Qt Property Browser的通用属性系统:Any类与向量/颜色属性的完美结合
  • 学习嵌入式第二十五天
  • QT QVersionNumber 比较版本号大小
  • office卸载不干净?Office356卸载不干净,office强力卸载软件下载
  • MySQL 索引(重点)
  • AT24C02C-SSHM-T用法
  • leecode875 爱吃香蕉的珂珂
  • 每日一题:2的幂数组中查询范围内的乘积;快速幂算法
  • 工业数采引擎-通信协议(Modbus/DTU/自定义协议)
  • 【Linux】重生之从零开始学习运维之防火墙
  • C++ 限制类对象数量的技巧与实践
  • AcWing 6479. 点格棋
  • ​费马小定理​
  • 前端组件库双雄对决:Bootstrap vs Element UI 完全指南
  • Unknown collation: ‘utf8mb4_0900_ai_ci‘
  • 软考 系统架构设计师系列知识点之杂项集萃(121)
  • mysql基础(二)五分钟掌握全量与增量备份
  • OCSSA-VMD-Transformer轴承故障诊断,特征提取+编码器!
  • 视频剪辑的工作流程