核心原则:为什么查询 Parse Error 如此特殊?
关键原因
:Parse Error(解析错误)发生在 SQL 语句的
语法检查/语义分析
阶段。在这个阶段,SQL 语句
根本没有成功进入共享池(Shared Pool)
,因此像
V$SQL
、
DBA_HIST_SQLTEXT
这样的常规视图
无法捕获到它们
。
Oracle 版本与核心方法速览
| 方法 | Oracle 12.2 之前 | Oracle 12.2 及之后 | 推荐度 |
|---|---|---|---|
| 方法A:手动开启 10035 事件 | 唯一官方手段 | 依然有效 | ★★★★★(通用) |
| 方法B:利用内置阈值告警 | 不支持 |
推荐
(
_kks_parse_error_warning
) |
★★★★★(12.2+) |
| 方法C:利用 Alert Log 中的地址反查 | 需配合 10035 | 需配合方法A或B | ★★★★(定位源头) |
方法详解与实战步骤
方法A:开启 10035 事件(最通用)
10035 事件会强制数据库将所有解析错误的详细信息实时写入 Alert Log(告警日志)。
操作流程:
-
开启捕获 (根据问题范围选择):
-- 会话级:只抓当前会话的错误,最安全ALTER SESSION SET events '10035 trace name context forever, level 1';-- 系统级:抓全库所有会话的错误,谨慎使用(排障期间短时开启)-- ALTER SYSTEM SET events '10035 trace name context forever, level 1';
-
复现问题 :让应用程序或业务系统正常运行,触发解析错误。
-
查看 Alert Log :
# 使用 tail 实时查看或 grep 过滤历史tail -f $ORACLE_BASE/diag/rdbms/<SID>/<SID>/trace/alert_<SID>.log | grep -A2 "PARSE ERROR"# 或一次性提取所有解析错误grep -A1 "PARSE ERROR" alert_<SID>.log > /tmp/parse_errors.log
-
关闭捕获 :
ALTER SESSION SET events '10035 trace name context off';-- 或 ALTER SYSTEM SET events '10035 trace name context off';
输出示例:
PARSE ERROR: ospid=4577, error=923 for statement: select count(*) frm dual
-
ospid:操作系统进程ID。 -
error:Oracle 错误码(如 942:表不存在;923:语法错误;904:列名无效)。 -
statement::后面就是导致解析失败的 完整 SQL 文本 。
优点
:直接、通用,能立即看到错误SQL。
缺点
:开启期间会记录
所有
解析错误,可能使 Alert Log 急剧增长。
方法B:利用 12.2+ 内置阈值(最省心)
从 Oracle 12.2 开始,引入了一个隐含参数
_kks_parse_error_warning
(默认值 100)。当
同一条 SQL 语句
在短时间内解析失败次数达到此阈值时,数据库会自动在 Alert Log 中打印警告信息,
无需手动开启 10035 事件
。
操作流程:
-
查看 Alert Log ,寻找类似条目:
ORCL(3):WARNING: too many parse errors, count=100 SQL hash=0x19a22496 ORCL(3):PARSE ERROR: ospid=4577, error=923 for statement: ORCL(3):select count(*) frm dual
它会直接输出 SQL 语句。
-
(可选)调整灵敏度 :如果想更早捕获,可以降低阈值(需要重启):
ALTER SYSTEM SET "_kks_parse_error_warning"=10 SCOPE=spfile;
注意 :这是隐含参数,修改需谨慎。
优点
:自动化,无性能开销,不污染日志。
缺点
:仅限 12.2+;需达到阈值才记录,可能错过偶发错误。
方法C:利用 Alert Log 中的地址反查(定位问题代码)
当你在 Alert Log 中看到
PARSE ERROR
时,通常会伴随
hd
和
phd
地址信息。这两个地址是
内存中游标对象的指针
,可以用来反向查询到
是谁在执行这条错误SQL
(尤其当 SQL 是动态拼接在匿名块或过程中时)。
核心概念:
-
phd: 父游标地址 (KGLHDPAR),对应V$SQLAREA.ADDRESS。 -
hd: 子游标地址 (KGLHDADR),对应V$SQL.CHILD_ADDRESS。
操作流程:
-
从 Alert Log 中提取地址 :
Additional information: hd=0x7673c258 phd=0x765151a8 ...
-
转换地址格式 :去掉
0x,左侧补0至16位(64位系统)。-
hd=0x7673c258->'000000007673C258' -
phd=0x765151a8->'00000000765151A8'
-
-
执行反查 :
-- 方案1:查询父游标(最常用,通常能定位到调用它的PL/SQL块)SELECT sql_id, sql_text, users_executing, loads, parse_callsFROM v$sqlareaWHERE address = HEXTORAW('00000000765151A8'); -- phd-- 方案2:精确查询子游标(如果还在内存中)SELECT child_address, plan_hash_value, executions, parse_calls, sql_textFROM v$sqlWHERE child_address = HEXTORAW('000000007673C258'); -- hd-- 方案3:终极兜底,查询内核对象表 x$kglob (需SYSDBA权限)-- 当V$SQL中记录已被刷出时使用SELECT kglnaobj AS sql_text, kglobt03 AS execs, kglobt04 AS parsesFROM x$kglobWHERE kglhdadr = HEPTORAW('000000007673C258') -- hd AND kglhdpar = HEXTORAW('00000000765151A8'); -- phd
示例结果 :通过查询父游标,你可能会发现错误的 SQL 被包裹在一个循环执行的匿名 PL/SQL 块中,从而找到问题根源。
总结与最佳实践建议
-
诊断流程 :
-
第一步(所有版本) :检查 Alert Log,搜索
PARSE ERROR或too many parse errors。 -
第二步(12.2+) :如果日志中已有自动记录,直接分析 SQL 和错误码。
-
第三步(若无记录) :在业务低峰期, 短时间开启会话级 10035 事件 ,复现问题后立即关闭。
-
第四步(定位根源) :利用 Alert Log 中打印的
hd/phd地址,反查V$SQLAREA,找到调用该错误 SQL 的上级代码(PL/SQL块、应用模块等)。
-
-
关键要点 :
-
10046 事件不行 :10046 是 SQL 跟踪,用于已成功解析的语句。解析错误发生在更早阶段,必须用 10035 。
-
及时关闭 :使用 10035 事件后务必关闭,避免日志爆炸。
-
结合 AWR/ADDM :如你所说,
failed parse指标高、latch: shared pool等待、ADDM 建议,这些都是需要调查解析问题的 信号 ,但具体 SQL 仍需通过上述方法捕获。 -
错误码解读 :用
oerr ora <错误码>命令快速了解错误原因(如oerr ora 942)。
-
通过这套组合拳,你就能精准地抓出那些“看不见”的解析错误 SQL,并定位到问题源头,从而进行修复或优化。