awr默认不捕获存储过程内联sql,因其仅记录直接提交到共享池的独立sql语句,而存储过程内的sql作为pl/sql单元编译缓存,执行时走v$sqlarea中pl/sql entry/execution类型游标,该类游标默认不入库awr快照。

AWR 默认不捕获存储过程内联 SQL 的根本原因
不是 AW R“抓不到”,而是它压根没打算抓——DBA_HIST_SQLSTAT 只记录**直接提交到共享池的独立 SQL 语句**,而存储过程里的 SQL 是作为 PL/SQL 单元的一部分被编译、缓存的,执行时走的是 v$sqlarea 中的 PL/SQL ENTRY 或 PL/SQL EXECUTION 类型游标,这类游标默认不入库 AWR 快照。
更关键的是:即使某条内部 SQL 被单独硬解析(比如动态拼接),它也只在 v$sql 里短暂存在;一旦被老化或刷新,AWR 就彻底失去上下文。所以你在 AWR 报告里看到的“Top SQL”,几乎全是应用直连执行的语句,不是 EXEC my_pkg.do_something 里藏的那几十行 SELECT。
怎么让 AWR 看见存储过程里的 SQL
没有银弹,但有两条可落地的路径:
- 强制把关键内部 SQL “拎出来”单独执行:把存储过程中高频、高耗的 SELECT/UPDATE 改成调用
DBMS_SQL或EXECUTE IMMEDIATE+ 绑定变量,并确保语句文本稳定(避免拼接字段名、表名)。这样它就变成独立游标,能进 AWR。 - 启用 SQL 跟踪并关联到 AWR:在调用存储过程前,用
DBMS_MONITOR.SERV_MOD_ACT_TRACE_ENABLE打开服务+模块级跟踪,生成 trace 文件后用tkprof提取真实执行的 SQL 和执行计划;再用sql_id去查DBA_HIST_SQLTEXT和DBA_HIST_SQLSTAT——前提是这条 SQL 确实被 AWR 捕获过(需满足执行次数 ≥ 5 或消耗资源超阈值)。
别试 ALTER SYSTEM SET "_cursor_plan_unload_time"=0 这种黑参数,它会让共享池更不稳定,且不保证 AWR 补录。
查不到 SQL_ID 时,怎么反向定位问题语句
当 AWR 报告只显示 PL/SQL 占 CPU 高,但找不到具体 SQL,按这个顺序查:
- 先看
DBA_HIST_ACTIVE_SESS_HISTORY:过滤session_type = 'FOREGROUND'且program LIKE '%my_pkg%',找sql_id非空的采样行——哪怕只有一两行,也能锁定真实执行的语句。 - 再查
v$session_longops(运行时):如果存储过程执行慢,大概率会出现在这里,opname和target能暴露它正在扫哪张表或做哪个排序。 - 最后翻包体源码 +
DBA_SOURCE:结合 AWR 中该存储过程的module和action字段(需应用层设置),定位到具体函数,再逐行检查是否有隐式转换、NVL、函数索引未生效等常见坑。
注意:v$sql 里查到的 sql_id 可能和 AWR 里的不一致——因为 PL/SQL 内部 SQL 的哈希计算会包含匿名块上下文,导致同一语句在不同包里生成不同 sql_id。
为什么 DISPLAY_AWR 查不到历史执行计划
DBMS_XPLAN.DISPLAY_AWR 返回空,通常不是语句没执行,而是它没被 AWR 认为“值得存”:
- 执行次数太少:AWR 默认只保留执行 ≥ 5 次的 SQL(受
_awr_sql_cost_threshold控制,不可改);单次调用存储过程里的 SQL,大概率被过滤。 - 没走标准解析路径:用了
/*+ NO_STATEMENT_QUEUING */、RESULT_CACHE或WITH_PLSQL特性,会导致优化器跳过常规计划生成流程。 - SQL 文本含非常规字符:比如 PL/SQL 块里拼了 chr(10) 换行符,
sql_id计算结果和你从源码复制的不一致,查DBA_HIST_SQLTEXT时要用dbms_lob.substr(sql_text, 200, 1)对比开头。
这时候别硬刚 AWR,直接在业务低峰期对存储过程做一次 DBMS_MONITOR.SESSION_TRACE_ENABLE,拿到真实执行计划最可靠。











