要让ef core生成的sql被oracle awr捕获,需确保硬解析发生:禁用语句缓存(statement cache size=0)、启用statistics_level=all,并用“/+ monitor /”探针语句绑定会话;同时关闭ef core列别名随机化、避免高频新会话,结合v$sql实时定位sql_id。
怎么让 ef core 生成的 sql 被 oracle awr 捕获到
oracle awr 默认只捕获「已解析且执行过」的 sql,而 ef core 的参数化查询(尤其带 bind variables 的)在 oracle 中常以不同 sql id 出现,导致 awr 里找不到对应记录。关键不是“怎么查”,而是“怎么让 sql 留下可追踪痕迹”。
实操建议:
- 确保数据库连接字符串中启用
Statement Cache Size=0(或设为较小值),避免 Oracle 客户端缓存语句后绕过硬解析——AWR 只对硬解析/软解析后的执行统计完整 - 在 EF Core 中主动触发一次
ExecuteSqlRaw执行带注释的“探针语句”,例如:context.Database.ExecuteSqlRaw("/*+ MONITOR */ SELECT 1 FROM DUAL");,该提示能让 Oracle 在 AWR 中强制关联后续同会话的 SQL(需配合STATISTICS_LEVEL = ALL) - 确认数据库参数
STATISTICS_LEVEL是ALL或TYPICAL(BASIC会禁用 AWR 的 SQL 统计)
如何从 EF Core 日志里拿到真实发给 Oracle 的 SQL
EF Core 默认日志里的 SQL 是带 @p0、@p1 占位符的模板,不能直接扔进 SQL*Plus 或 AWR 报告里验证。你真正需要的是「带实际参数值的可执行语句」。
实操建议:
- 不要依赖
LogTo(Console.WriteLine)的默认输出——它不展开参数。改用DbContextOptionsBuilder.EnableSensitiveDataLogging(true),再配合自定义IDbCommandInterceptor - 在拦截器的
ReaderExecuted方法里,通过command.Parameters遍历并拼出带值的 SQL(注意 Oracle 的DATE和CHAR类型要加单引号和TO_DATE包裹) - 警惕
ToString()直接调用:Oracle 的OracleParameter.Value可能是DBNull.Value或DateTimeKind.Unspecified,直接拼串会抛异常或生成错误 SQL
为什么在 AWR 报告里搜不到 EF Core 的 SQL_ID
不是 SQL 没执行,而是 Oracle 认为它是“新语句”——因为 EF Core 默认开启 EnableRetryOnFailure 或使用了 TransactionScope,导致每次执行都走新会话;或者用了 AsNoTracking() 后触发了隐式游标关闭逻辑,中断了 SQL ID 的复用链。
实操建议:
- 检查 AWR 报告时间范围是否覆盖了实际执行窗口(默认每小时快照一次,但 SQL 可能只在某几分钟内高频执行)
- 用
SELECT sql_id, sql_text FROM v$sql WHERE sql_text LIKE '%your_table_name%'手动查内存视图,比等 AWR 更快定位——AWR 是采样汇总,v$sql是实时缓存 - 如果 SQL 文本里有 EF Core 自动生成的 GUID 别名(如
[t0].[c2f8a7e5...]),会导致每条语句都被视为唯一,此时应在 EF Core 配置中禁用列别名随机化:UseOracleSQLBuilder替换为自定义 builder,或升级到 EF Core 8+ 并设置UseRelationalNulls(false)减少表达式变异
怎么拿到 Oracle 执行计划并和 EF Core 查询对齐
直接在 Oracle 里用 EXPLAIN PLAN FOR ... 不行——EF Core 发的是绑定变量语句,而 EXPLAIN PLAN 对绑定变量不做窥探(bind peeking),计划可能和真实执行偏差极大。
实操建议:
- 用
DBMS_XPLAN.DISPLAY_CURSOR(sql_id, child_number, 'ALLSTATS LAST')查真实执行过的计划,其中sql_id来自v$sql,child_number选0(最常用子游标) - 在 EF Core 查询前加
context.Database.BeginTransaction(),再用SELECT * FROM v$session WHERE sql_id IS NOT NULL AND sid = SYS_CONTEXT('USERENV', 'SID')锁定当前会话的sql_id,确保计划来源唯一 - 注意
ROWS列在ALLSTATS LAST中显示的是实际返回行数,和ESTIMATED_OPTIMAL_SIZE对比,才能判断是否因统计信息陈旧导致计划劣化——这比看“有没有全表扫描”有用得多
最麻烦的地方往往不在工具链,而在 Oracle 对绑定变量的处理粒度和 EF Core 运行时对连接生命周期的控制——这两者稍有错位,SQL 就像进了黑洞,既不在日志里显形,也不在 AWR 中留痕。










