静态sql在存储过程中需完整执行且路径命中才写入v$sql,查执行计划须满足:成功执行、路径覆盖、禁用autonomous_transaction,并用v$sqlarea/v$sql精准定位sql_id后,以正确参数调用dbms_xplan.display_cursor。

存储过程里静态SQL不进v$sql,得先执行一遍
直接在存储过程中调用 DBMS_XPLAN.DISPLAY_CURSOR 查不到目标 SQL 的执行计划,根本原因是:静态 SQL 在编译或声明阶段不会生成游标,只有运行到那条语句时,才会硬解析并写入 v$sql。哪怕你刚 EXEC 完存储过程,只要过程中途异常退出、或没走到目标分支,v$sql 里就大概率没有对应记录。
实操上必须满足三个前提:
- 存储过程已完整成功执行一次(不是只调用、没走完逻辑)
- 目标 SQL 所在的代码路径确实被执行(比如
IF ... THEN ... ELSE分支里,确认走的是含该 SQL 的分支) - 避免用
PRAGMA AUTONOMOUS_TRANSACTION包裹目标 SQL——它会切换事务和会话上下文,导致游标归属分离,查不到计划
查不到 sql_id?别瞎猜,从 v$sqlarea 或 v$sql 精准捞
DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST') 基本无效:它返回的往往是 CALL 或 BEGIN ... END 这类 PL/SQL 块本身的游标,不是你想要的 SELECT/UPDATE 语句。
正确做法是立刻查缓存视图,按时间+文本双维度过滤:
- 执行完存储过程后,马上运行:
SELECT sql_id, child_number, sql_text FROM v$sqlarea WHERE sql_text LIKE '%your_table_name%' AND last_active_time > SYSDATE - 1/1440 ORDER BY last_active_time DESC; - 如果 SQL 含绑定变量,
sql_text显示的是:B1、:B2这类占位符,不是明文值,别指望靠值匹配 - 要区分子游标(同一
sql_id可能有多个child_number),查v$sql比v$sqlarea更细粒度;但注意需结合address和hash_value判断是否真重复
DISPLAY_CURSOR 参数填错就白忙,顺序和类型必须严丝合缝
DBMS_XPLAN.DISPLAY_CURSOR 三个参数顺序固定、类型敏感,填错任意一个就会返回空、报 ORA-01002 或显示无关计划:
- 第一个参数是
sql_id(字符串),不能传数字或空字符串,NULL表示“当前会话最后执行的 SQL”——但如前所述,这通常不是你要的 - 第二个参数是
cursor_child_no(数字或NULL),不是字符串;不填默认为 0,但如果该sql_id下 child_number=1 的才是目标计划,就会漏掉 - 第三个参数是 format 字符串,常用
'ALLSTATS LAST'(带实际 I/O 和行数)、'ADVANCED'(含 Outline Data),拼错大小写或加空格都可能失败
典型可用语句:SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('abc123xyz456', 1, 'ALLSTATS LAST'));
STATISTICS_LEVEL 必须设为 ALL,否则 ALLSTATS 类 format 不生效
'ALLSTATS LAST' 这类 format 要求 Oracle 在执行时收集运行时统计信息,而默认的 STATISTICS_LEVEL=TYPICAL 不会捕获所有字段(比如 A-ROWS、Buffers、Reads)。不设这个,即使参数全对、sql_id 正确,输出里也看不到实际行数和物理读等关键指标。
执行前务必确认会话级设置:
ALTER SESSION SET STATISTICS_LEVEL = ALL;- 注意:这不是永久设置,只对当前会话有效;也不建议全局改,会影响性能
- 如果过程本身很长或含多条 SQL,建议在目标 SQL 执行前再设一次,避免被其他操作覆盖
真正难的不是语法,而是确保那条 SQL 已落地为可查的游标、且上下文没被事务隔离或共享池老化抹掉——每一步都得验证,不能靠“应该有了”。











