You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Oracle查询性能调优:如何稳定使用高效Execution Plan?

Oracle查询性能调优:避免低效执行计划(全表扫描、笛卡尔积合并)

问题背景

有一条Oracle查询语句,在部分环境中偶尔会生成性能极差的执行计划,出现Table Access Full(全表扫描)和Merge Join Cartesian(笛卡尔积合并),相关表的统计数据已更新,需要找出SQL中的低效点并给出调优方案,确保持续使用高效执行计划。

原查询语句:

select 
 'RECEIVE-DONE' OP_TYPE,   
 sbmt.SUBMIT_NO, 
 wsr.USE_LUMP_PROC, 
 sf.* 
from ( 
 select 
 my.flow_id myflow_id, 
 done.flow_id flow_id, 
 done.status SFSTATUS, 
 from submit_flow my , submit_flow done 
  where my.submit_id=done.submit_id 
  and my.check_ord=done.check_ord 
  and my.emp_id = '1'
  and done.status
  in (1,2,3,4) 
  and ( (done.mainflow=1 and done.check_ord > 0) or (done.emp_id ='1' and done.type in (1,2,3) and done.type = my.type ) )  
  ) sf,  
  submit_list sbmt, wfserveroute wsr, emp emp 
where  
 sbmt.EMP_ID = emp.EMP_ID 
 and sf.SUBMIT_ID = sbmt.SUBMIT_ID 
 and ((sf.TYPE = 1 and sbmt.COMPLETED = 1 and sbmt.status = 2) or (sf.TYPE <> 1)) 
 and sbmt.FLOW_SCHEM  = wsr.SCHEM   
 and sbmt.SERVICENAME = wsr.FULLNAME 
 and wsr.CASE = 0 
 and not exists( 
 SELECT 1 FROM service_mst smst 
 WHERE   (smst.namespace||'.'||smst.name) = sbmt.SERVICENAME
 and  smst.not_view_at_inouttray = 1  
 )  order by sf.prc_date DESC

一、SQL中的明显低效/冗余点

  • 自关联条件冗余:submit_flow自关联时,my.check_ord=done.check_ord已被包含在后续分支条件中,重复关联可能干扰优化器对数据量的判断。
  • 字符串拼接导致索引失效:not exists子查询中使用smst.namespace||'.'||smst.name = sbmt.SERVICENAME,拼接操作会阻止Oracle使用smst.namespace或smst.name上的索引,强制触发全表扫描。
  • OR条件破坏索引有效性:主查询的((sf.TYPE = 1 and sbmt.COMPLETED = 1 and sbmt.status = 2) or (sf.TYPE <> 1))分支,让优化器难以选择合适索引,易触发全表扫描。
  • 隐式关联导致笛卡尔积风险:使用逗号分隔表的旧风格关联,未显式指定JOIN类型,优化器可能误判表数据量,选择笛卡尔积合并。

二、具体调优步骤

1. 优化自关联子查询,简化条件

将submit_flow自关联改为显式INNER JOIN,去除重复条件,只保留外层需要的字段,减少冗余计算:

select 
 my.flow_id myflow_id, 
 done.flow_id flow_id, 
 done.status SFSTATUS,
 done.submit_id, done.type, done.prc_date
from submit_flow my
inner join submit_flow done 
  on my.submit_id = done.submit_id
  and my.check_ord = done.check_ord
  and my.emp_id = '1'
where done.status in (1,2,3,4)
  and (
    (done.mainflow = 1 and done.check_ord > 0) 
    or (done.emp_id = '1' and done.type in (1,2,3) and done.type = my.type)
  )

2. 修复字符串拼接导致的索引失效

方案一:创建基于函数的索引

create index idx_smst_namespace_name on service_mst (namespace||'.'||name, not_view_at_inouttray);

方案二:新增冗余字段+普通索引(推荐,性能更优)

alter table service_mst add full_service_name varchar2(200);
update service_mst set full_service_name = namespace||'.'||name;
create index idx_smst_fullname on service_mst (full_service_name, not_view_at_inouttray);

之后将not exists子查询条件改为smst.full_service_name = sbmt.SERVICENAME。

3. 拆分OR条件,用UNION ALL替代

OR条件是优化器的常见陷阱,将查询拆分为两个逻辑分支,用UNION ALL合并(确保分支无重复数据):

-- 分支1:sf.TYPE = 1的场景
select 
 'RECEIVE-DONE' OP_TYPE,   
 sbmt.SUBMIT_NO, 
 wsr.USE_LUMP_PROC, 
 sf.* 
from (
 -- 引用优化后的自关联子查询
) sf
inner join submit_list sbmt on sf.SUBMIT_ID = sbmt.SUBMIT_ID
inner join emp emp on sbmt.EMP_ID = emp.EMP_ID
inner join wfserveroute wsr 
  on sbmt.FLOW_SCHEM = wsr.SCHEM 
  and sbmt.SERVICENAME = wsr.FULLNAME
where sf.TYPE = 1 
  and sbmt.COMPLETED = 1 
  and sbmt.status = 2
  and wsr.CASE = 0
  and not exists(
    SELECT 1 FROM service_mst smst 
    WHERE smst.full_service_name = sbmt.SERVICENAME
    and smst.not_view_at_inouttray = 1  
  )

union all

-- 分支2:sf.TYPE <> 1的场景
select 
 'RECEIVE-DONE' OP_TYPE,   
 sbmt.SUBMIT_NO, 
 wsr.USE_LUMP_PROC, 
 sf.* 
from (
 -- 引用优化后的自关联子查询
) sf
inner join submit_list sbmt on sf.SUBMIT_ID = sbmt.SUBMIT_ID
inner join emp emp on sbmt.EMP_ID = emp.EMP_ID
inner join wfserveroute wsr 
  on sbmt.FLOW_SCHEM = wsr.SCHEM 
  and sbmt.SERVICENAME = wsr.FULLNAME
where sf.TYPE <> 1
  and wsr.CASE = 0
  and not exists(
    SELECT 1 FROM service_mst smst 
    WHERE smst.full_service_name = sbmt.SERVICENAME
    and smst.not_view_at_inouttray = 1  
  )

order by prc_date DESC

拆分后每个分支条件明确,优化器可选择对应索引,避免全表扫描。

4. 显式指定JOIN类型,规避笛卡尔积

将原查询的逗号分隔表关联改为显式INNER JOIN,明确表之间的依赖关系,让优化器清晰判断关联逻辑:

from (优化后的自关联子查询) sf
inner join submit_list sbmt on sf.SUBMIT_ID = sbmt.SUBMIT_ID
inner join emp emp on sbmt.EMP_ID = emp.EMP_ID
inner join wfserveroute wsr 
  on sbmt.FLOW_SCHEM = wsr.SCHEM 
  and sbmt.SERVICENAME = wsr.FULLNAME

5. 创建关键复合索引

为以下字段创建复合索引,覆盖过滤、关联和排序需求:

  • submit_flow:(emp_id, submit_id, check_ord, status, mainflow, type, prc_date)
  • submit_list:(SUBMIT_ID, TYPE, EMP_ID, COMPLETED, status, FLOW_SCHEM, SERVICENAME, SUBMIT_NO)
  • wfserveroute:(SCHEM, FULLNAME, CASE, USE_LUMP_PROC)
  • emp:确保EMP_ID有唯一索引(若主键不是该字段)

6. 锁定高效执行计划(应对环境差异导致的计划漂移)

如果部分环境仍出现执行计划波动,可通过以下方式锁定计划:

  • 使用DBMS_SQLTUNE.CREATE_SQL_PROFILE生成SQL Profile并绑定到目标查询,强制优化器选择高效计划。
  • 使用/*+ USE_PLAN(...) */提示,直接指定高效执行计划的XML格式(需先获取目标计划的XML)。

内容的提问来源于stack exchange,提问作者user21858464

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 10:05:40