v$sqlarea无法反映pl/sql块完整执行时间,因其按sql_text聚合且pl/sql被拆为多条子语句缓存;应通过command_type=47和parsing_schema_name筛选,并关联v$sql_plan与awr分析内嵌sql瓶颈。

查 V$SQLAREA 时为什么看不到 PL/SQL 块的完整执行时间
V$SQLAREA 的 elapsed_time 字段只记录单次 SQL 语句(非 PL/SQL)的解析+执行耗时,而 PL/SQL 块(如 BEGIN ... END;)在共享池中通常被拆成多条子语句缓存,每条对应一个独立的 sql_id;V$SQLAREA 按 sql_text 聚合,但 PL/SQL 块首行常是 BEGIN 或 DECLARE,后续语句文本不同,根本不会被归到同一行。所以直接查 ORDER BY elapsed_time DESC 几乎找不到真正慢的 PL/SQL 块本身。
用 parsing_schema_name + command_type 锁定 PL/SQL 执行上下文
PL/SQL 块编译或执行时,parsing_schema_name 是调用者 schema,command_type 为 47(代表 PL/SQL BLOCK)。这是最可靠的筛选锚点:
-
command_type = 47是硬性过滤条件,避免把普通 SELECT/INSERT 当成 PL/SQL - 必须加
parsing_schema_name IS NOT NULL,否则可能混入系统内部递归调用 - 不要依赖
sql_text LIKE 'BEGIN%'—— 实际存储的sql_text可能被截断(仅前 1000 字符),且空格、换行、注释会导致匹配失败
示例查询:
SELECT sql_id, sql_text, executions,
ROUND(elapsed_time / 1000000 / NULLIF(executions, 0), 2) avg_sec,
first_load_time, last_active_time
FROM v$sqlarea
WHERE command_type = 47
AND parsing_schema_name = 'YOUR_SCHEMA'
AND executions > 0
ORDER BY avg_sec DESC
FETCH FIRST 10 ROWS ONLY;
查到 sql_id 后必须立刻关联 v$sql_plan 和 AWR
V$SQLAREA 返回的只是聚合快照,对 PL/SQL 块尤其危险:它不保存执行计划路径,而 PL/SQL 性能瓶颈往往卡在某条内嵌 SQL 的全表扫描或索引失效上。
- 先用
SELECT * FROM v$sql_plan WHERE sql_id = '<your_sql_id>' AND operation = 'TABLE ACCESS' AND options = 'FULL'</your_sql_id>确认是否有隐式全扫 - 再查 AWR:用
DBMS_XPLAN.DISPLAY_AWR('<sql_id>')</sql_id>看历史执行计划,因为 PL/SQL 块可能已老化出共享池,但 AWR 里还留着上周的真实计划 - 注意
last_active_time可能是几小时前——PL/SQL 块执行完就释放资源,不能靠这个判断“当前是否还在跑”
真正慢的 PL/SQL 往往不在 V$SQLAREA 里
如果业务里有长事务型 PL/SQL(比如循环处理百万级数据),它大概率不会出现在 V$SQLAREA 中,原因很实际:
- 执行时间过长导致游标被老化(aging out),共享池腾空间时直接丢弃 PL/SQL 块若含动态 SQL(
- 此时该转向
v$session_longops查实时进度,或用DBA_HIST_ACTIVE_SESS_HISTORY回溯过去 1 小时内的活跃会话堆栈
EXECUTE IMMEDIATE),每次拼接生成新 sql_text,无法被 V$SQLAREA 归并,变成一堆低 executions 的碎片记录最易被忽略的一点:V$SQLAREA 里看到的高 elapsed_time PL/SQL 记录,90% 是因为其中某条内嵌 SQL 被反复调用,而不是 PL/SQL 引擎本身慢——盯住子语句的 sql_id,别在块头打转。











