在数据库运维过程中,DBA经常会遇到这样的情况:一条原本运行正常的SQL,在统计信息变更、参数调整或数据库升级之后,突然出现ORA-00600、ORA-07445等内部错误,或者执行计划急剧恶化导致业务卡死。这类问题往往来不及深入分析根因,业务方要求第一时间恢复。Oracle 11g引入的SQL Repair Advisor正是针对这类场景设计的应急工具,它通过SQL诊断器的修复建议,在不改动应用代码的情况下让问题SQL恢复可用。

SQL Repair Advisor的工作原理
SQL Repair Advisor属于Oracle 11g诊断框架的一部分,底层依赖SQL Diagnostic Analyzer实现。当一条SQL出现执行计划引发的错误时,DBA可以针对这条SQL创建一个诊断任务,数据库会在内部对这条SQL进行多种场景的模拟测试,例如禁用某个优化器特性、强制使用不同的优化器参数、尝试不同的执行路径等,然后评估每种场景下的执行结果,最终给出可行的修复建议。
修复建议通常包含两类:一是针对Oracle 7445、600这类内部错误,advisor可能建议禁用某个具体的优化器特性,比如禁用布尔谓词传播、查询转换等,这样执行计划会绕开触发bug的路径;二是针对性能突然恶化的情况,advisor可能给出一个替代的执行计划。这些修复建议最终会以SQL Patch的形式存储在数据字典中,之后每当这条SQL被硬解析时,优化器会自动应用这个patch中记录的hint或参数覆盖,从而改变执行计划的生成过程。
需要注意的关键一点是,SQL Patch是绑定在SQL文本上的。只要SQL语句的文本完全一致,即使来自不同的会话、不同的模块,patch都会生效。这一点与存储大纲类似,但SQL Patch的创建和管理方式更加灵活,可以通过DBMS_SQLDIAG包随时创建、启用和删除。
使用DBMS_SQLDIAG创建修复任务的完整步骤
整个操作流程分为四步:找到问题SQL的SQL_ID、创建诊断任务、执行任务并查看报告、接受修复建议。假设一条SQL持续报ORA-00600错误,首先需要从告警日志或者V$SQL视图中定位到它的SQL_ID和执行计划hash值。
第二步是创建诊断任务,调用DBMS_SQLDIAG.CREATE_DIAGNOSIS_TASK函数:
DECLARE
v_task_name VARCHAR2(100);
BEGIN
v_task_name := DBMS_SQLDIAG.CREATE_DIAGNOSIS_TASK(
sql_text => 'SELECT * FROM orders o, order_items i WHERE o.order_id = i.order_id AND o.status = :1',
bind_list => SQL_BINDS(anydata.ConvertVarchar2('SHIPPED')),
time_limit => 600,
task_name => 'sql_repair_task_01',
problem_id => 0
);
END;
/任务创建后需要执行它,执行完毕后通过REPORT_DIAGNOSIS_TASK查看分析报告:
-- 执行诊断任务
BEGIN
DBMS_SQLDIAG.EXECUTE_DIAGNOSIS_TASK(task_name => 'sql_repair_task_01');
END;
/
-- 查看诊断报告
SELECT DBMS_SQLDIAG.REPORT_DIAGNOSIS_TASK(
task_name => 'sql_repair_task_01',
type => DBMS_SQLDIAG.TYPE_TEXT,
level => DBMS_SQLDIAG.LEVEL_ALL,
section => DBMS_SQLDIAG.SECTION_ALL
) AS report FROM dual;
/报告中会列出advisor测试过的各种替代方案,以及每个方案是否成功执行。找到成功的修复方案后,调用ACCEPT_SQL_PATCH过程接受它,数据库会自动创建对应的SQL Patch:
BEGIN
DBMS_SQLDIAG.ACCEPT_SQL_PATCH(
task_name => 'sql_repair_task_01',
object_id => 1, -- 报告中成功的方案编号
rank => 1,
attribute_name => NULL,
attribute_value => NULL
);
END;
/接受建议之后,可以通过DBA_SQL_PATCHES视图确认patch已经创建。此时重新执行问题SQL,如果还报同样的错误,先做一次硬解析即可,因为patch只在硬解析阶段被应用。可以通过ALTER SYSTEM FLUSH SHARED_POOL或者对相关表做DDL触发重新解析。
验证与管理SQL Patch
修复是否生效,最直接的验证方法是查看新执行计划中是否使用了patch。对SQL执行DBMS_XPLAN.DISPLAY_CURSOR,在Note部分如果看到SQL patch used for this statement的提示,说明patch已经生效并影响了执行计划的生成。
SELECT * FROM TABLE(
DBMS_XPLAN.DISPLAY_CURSOR(
sql_id => 'd4c9kfk0jwgx9',
format => 'TYPICAL +PATTERN'
)
);日常管理方面,DBMS_SQLDIAG包提供了ALTER_SQL_PATCH过程用于启用或禁用patch,DROP_SQL_PATCH用于删除patch。比如确认根因已修复、数据库打了相关补丁之后,就应该及时删除patch,避免残留的patch干扰正常优化器行为:
BEGIN
DBMS_SQLDIAG.DROP_SQL_PATCH(
name => 'SYS_SQLPTCH_01_XXXXXXXX',
force => FALSE
);
END;
/查询patch的使用情况可以借助V$SQL视图的SQL_PATCH列,它记录了每条SQL当前应用的patch名称,配合DBA_SQL_PATCHES视图可以完整掌握数据库中所有SQL Patch的状态、创建时间和来源。
SQL Repair Advisor与SQL Plan Management的区别和配合
很多DBA容易把SQL Repair Advisor创建的SQL Patch和SQL Plan Management创建的SQL Plan Baseline混为一谈。两者虽然都存储在SQLOBJ$相关底层表中,都以hint形式影响优化器,但定位完全不同。SQL Plan Baseline的目标是计划稳定性,防止执行计划退化,它保存的是经过验证的完整执行计划;而SQL Patch的目标是修复,它保存的往往是一条或几条针对特定问题的hint,比如禁用某个查询转换的OPT_PARAM类hint,或者一个强制索引访问的hint。
两者的触发方式也不同。Baseline在优化器生成计划后做匹配校验,如果生成的计划不在baseline中,会尝试走SQL Plan Matching流程;而Patch在SQL文本解析阶段就直接注入hint,相当于在语句外面隐式包了一层。因此在紧急故障场景下,SQL Patch的干预力度更直接,见效也更快。
在实际运维中,这两者可以配合使用。例如某条SQL因优化器新特性导致计划变差,可以先用SQL Repair Advisor创建patch快速止血,业务恢复后再从容分析根因。等确认了正确的执行计划后,通过DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE把它固化为baseline,然后删除临时patch,实现从应急处理到长期稳定的过渡。这种先patch后baseline的做法,在生产环境的版本升级和统计信息维护窗口中非常实用,既能快速恢复业务,又不会给系统留下难以维护的临时配置。
Oracle 11gSQL Repair AdvisorSQL修复修改时间:2026-09-06 12:56:35