ASH 默认不捕获 PL/SQL 内部 SQL 执行快照,仅记录会话顶层状态(如客户端 SQL 或 PL/SQL 入口点),不深入调用栈;需用 SQL Monitoring(满足耗时>5秒等条件)或 DBMS_PROFILER、SESSION_TRACE 等工具定位内部 SQL。
ASH 默认不捕获 PL/SQL 内部 SQL 的执行快照
oracle 12c 的 v$active_session_history 每秒只采样一次会话的“顶层状态”,即当前正在执行的调用栈最外层:要么是客户端直连发来的 sql,要么是 pl/sql 匿名块/过程的入口点。它**不会深入展开 pl/sql 执行体内部去抓子 sql 的执行瞬间**——哪怕那条 select 耗时 500ms,只要它嵌在 for rec in (select ...) 或 execute immediate 里,ash 样本中记录的 sql_id 通常是外层 pl/sql 单元的(如包体或过程名),而不是那条被循环执行的内部语句。
想看到 PL/SQL 内部 SQL,必须启用 SQL Monitoring(而非依赖 ASH)
ASH 是“会话级采样”,而真正能穿透 PL/SQL 层、捕获每条内部 SQL 执行细节的机制是 SQL Monitoring,前提是满足以下全部条件:
- SQL 执行时间 > 5 秒(默认阈值),或显式加
/*+ MONITOR */提示 - 数据库参数
STATISTICS_LEVEL必须为TYPICAL或ALL(BASIC会彻底禁用监控) - 不是所有 PL/SQL 场景都触发:例如
SELECT ... BULK COLLECT INTO中的查询可能被监控,但OPEN cur FOR 'SELECT...'+FETCH分步执行则大概率不进监控视图
查监控结果用:SELECT * FROM V$SQL_MONITOR WHERE sql_text LIKE '%your_inner_sql%',而不是查 V$ASH。
细粒度采样?别信名字,ASH 本身不支持“更细”
有人误以为调大 _ash_sampling_interval(隐藏参数)就能捕获 PL/SQL 内部 SQL,这是错的。该参数在 12c+ 已废弃,且即使早期版本生效,也只是让采样更频繁,**仍不改变采样对象层级**——它采的还是 session 级状态,不是 call stack 深度遍历。真正需要深度追踪时,应转向:
-
DBMS_MONITOR.SESSION_TRACE_ENABLE(带waits=>true, binds=>true)获取完整调用栈和绑定变量 - Oracle 12c+ 的
DBMS_SQLTUNE.CAPTURE_PLAN_BASELINE配合 SQL Plan Management,间接暴露高频内部 SQL - PL/SQL Profiler(
DBMS_PROFILER)——它专为代码行级耗时设计,但需提前编译带 profiler 支持的包,且不替代运行时 SQL 监控
容易忽略的关键点:PL/SQL 单元类型决定可见性
同一个 SQL 文本,在不同上下文中对 ASH “隐身”程度不同:
- 在匿名 PL/SQL 块中执行 →
sql_id通常为空或为系统生成的伪 ID,ASH 记录为PL/SQL execution - 在已编译的存储过程中执行 → 若过程未被频繁调用,其
sql_id可能根本不出现在 ASH 中(因采样只保最近活跃) - 通过
DBMS_SQL.PARSE动态执行 → 视为独立 SQL,有自己sql_id,但仅当该动态语句本身执行够久、触发 SQL Monitoring 才易被定位
所以,不要在 V$ASH 里死磕“为什么看不到某条内部 SQL”——先确认它是否真被当作独立可监控单元对待,再选对工具。ASH 的价值在于快速定位“谁在等什么”,而不是“函数里第 3 行 SELECT 花了多久”。


















