ASH通过plsql_entry_object_id和plsql_entry_subprogram_id关联存储过程,仅记录其内部执行的SQL及等待事件;需先查目标过程object_id,再在ASH中筛选匹配的活跃会话,并注意top_level_sql_id回溯调用链。
直接查 v$active_session_history,用 plsql_entry_object_id 和 plsql_entry_subprogram_id 关联到具体存储过程,是定位慢执行最有效的手段之一——前提是采样窗口内该过程确实在“活跃执行中”,而不是刚进就结束或处于 pl/sql 解析/上下文切换阶段。
怎么从 ASH 找到目标存储过程的 SQL 调用链
ASH 不记录存储过程本身,只记录它内部执行的 SQL(以及等待事件)。关键在于:存储过程调用时,其顶层 SQL 的 plsql_entry_object_id 会指向该过程的 object_id,plsql_entry_subprogram_id 指向子程序编号(如包体中的过程序号)。
- 先查出目标过程的
object_id:SELECT object_id FROM all_objects WHERE owner = 'YOUR_SCHEMA' AND object_name = 'YOUR_PROCEDURE_NAME' AND object_type = 'PROCEDURE' - 再查 ASH 中匹配的活跃会话:
SELECT sql_id, sql_plan_hash_value, event, p1, p2, p3, session_state, sample_time FROM v$active_session_history WHERE plsql_entry_object_id = <your_object_id> AND sample_time > SYSDATE - 1/24</your_object_id> - 如果过程在包里,还需确认
plsql_entry_subprogram_id(可通过all_procedures查,注意subprogram_id是从 1 开始的序号) - 别漏掉
top_level_sql_id字段——它指向最外层 SQL(比如调用该过程的匿名块里的EXEC),可帮你回溯调用来源
为什么查不到你的存储过程?常见断点位置
不是所有 PL/SQL 执行都会留下 ASH 痕迹。以下情况会导致“查无此过程”:
- 过程执行太快(RETURN 的分支
- 过程卡在 PL/SQL 层(比如循环、JSON 构造、正则匹配),没发出任何 SQL——此时
sql_id为NULL,session_state可能是ON CPU,但plsql_entry_object_id仍有效,需加sql_id IS NULL过滤 - 过程被硬解析或频繁重编译,导致
plsql_entry_object_id在采样瞬间未稳定(少见,但高并发重定义时会出现) - 你查的是
dba_hist_active_sess_history,但 AWR 快照间隔太长(如 1 小时),而问题发生在快照间隙
如何把 ASH 结果和真实 SQL 关联起来
拿到 sql_id 后,不能只看文本,必须结合执行上下文判断是否真属于该过程逻辑:
- 用
SELECT * FROM v$sql WHERE sql_id = '<xxx>'</xxx>看program_id和program_line#,它们会指向 PL/SQL 编译单元内的行号(需开启PLSQL_DEBUG或使用DBMS_OUTPUT配合) - 对比
v$sql_plan中的object_name和过程名是否一致(有时过程只是封装了对某张表的访问) - 重点看
event字段:如果是db file sequential read,结合p1(file#)、p2(block#)反查对象,常暴露底层表访问异常(如单块读延迟几十秒,大概率是物理 IO 或行迁移) - 若大量出现
cursor: pin S wait on X,说明该过程高频打开/关闭游标,或绑定变量未复用——这和plsql_entry_object_id强相关,但根源在代码写法
真正难的不是查到哪条 SQL 慢,而是区分“SQL 本身慢”和“被 PL/SQL 框架拖慢”。比如 apps.json.put 类操作,在 ASH 里看不到 SQL,但 session_state = ON CPU + plsql_entry_object_id 匹配,就是典型 PL/SQL 内部开销;这时候再翻执行计划已经没用了,得看过程内部结构和数据量级。


















