应先用explain plan for预编译目标sql(勿带绑定变量占位符),再查plan_table原始字段;statement_id需唯一并order by id,cardinality比rows更可靠,full扫描大表、深层嵌套、高基数排序等表明性能瓶颈。
oracle执行计划怎么看:先拿到explain plan结果
直接在c#里查不出执行计划,得先让oracle生成并存入plan_table。别用select * from table(dbms_xplan.display)这种写法去拼接sql再执行——它返回的是格式化文本,难解析。正确做法是用explain plan for语句预编译你的目标sql(注意:不能带绑定变量占位符如:p1,否则explain plan会报错或给出不准确计划),再查plan_table的原始字段。
常见错误是把C#中拼出的带参数SQL直接喂给EXPLAIN PLAN FOR,比如"SELECT * FROM orders WHERE id = " + id——这有SQL注入风险,且Oracle无法复用执行计划缓存。应该改用EXPLAIN PLAN FOR SELECT * FROM orders WHERE id = :id,然后用OracleCommand.BindByName = true配合AddWithValue传参(但注意:EXPLAIN PLAN本身不支持运行时绑定,所以这步只能在调试阶段手动替换参数值后执行)。
C#中如何安全获取和解析执行计划数据
用OracleCommand执行两步:先EXPLAIN PLAN SET STATEMENT_ID = 'mytest' FOR ...,再SELECT operation, options, object_name, cost, cardinality FROM plan_table WHERE statement_id = 'mytest' ORDER BY id。关键点:
-
STATEMENT_ID必须唯一,建议用GUID或时间戳,避免多线程冲突 - 查
plan_table时务必加ORDER BY id,否则父子关系乱序,看不出嵌套结构 -
cardinality列比rows更可靠,它是优化器预估的返回行数,若远小于实际返回量(比如查10万行却预估100),说明统计信息过期,该跑DBMS_STATS.GATHER_TABLE_STATS - 别依赖
cost绝对值,只看同一SQL不同写法间的相对变化
哪些执行计划特征说明SQL真有问题
不是所有高cost都慢,但以下模式基本等于性能瓶颈:
-
operation出现FULL且object_name是大表名 → 缺少索引或索引未被使用 -
options为ACCESS但object_name是函数索引名,而SQL里没用对应函数 → 函数索引白建了 - 同一SQL多次执行,
plan_table里id深度超过8层 → 嵌套子查询/连接太多,考虑拆成临时表或物化视图 -
operation含SORT JOIN或SORT GROUP BY且cardinality超10万 → 内存排序溢出到磁盘,检查PGA_AGGREGATE_TARGET设置
在.NET里做执行计划对比的实操技巧
想验证改写SQL是否真有效,别只看一次EXPLAIN PLAN。用C#写个简单对比工具:
① 把原SQL和改写SQL分别用不同STATEMENT_ID执行EXPLAIN PLAN;
② 查出两组operation+object_name+cost,用HashSet<string></string>比对访问路径差异;
③ 特别关注cardinality变化率:若从1000跳到50000,即使cost降了,实际可能更慢(优化器误判);
④ 记住:Oracle 12c+支持DBMS_XPLAN.DISPLAY_CURSOR查真实执行过的计划(需STATISTICS_LEVEL=ALL),比EXPLAIN PLAN准得多,但要求SQL已执行过至少一次。
最易忽略的是绑定变量窥探(bind peeking)——C#里第一次传id=1生成的计划,可能完全不适合后续传id=1000000。这时候EXPLAIN PLAN永远看不到真实问题,得切到DISPLAY_CURSOR并抓SQL_ID。











