Oracle环境下SQL Delete语句执行过慢(无记录仍耗时)求助
SQL删除语句优化方案
先修正核心逻辑错误
原SQL里的OR条件没有加括号,由于AND优先级高于OR,实际执行逻辑会偏离预期,变成:(ud2.udt02_id=ctree.charge_cd AND mo.s_mo_status_cd in ('L','C') AND mo.complt_dt <= SYSDATE -7) OR mo.allow_timesheet_fl='N'
这会导致大量无关的mo_hdr记录被关联,必须把OR的过滤逻辑用括号包裹,确保符合业务需求:
AND ( (mo.s_mo_status_cd IN ('L','C') AND mo.complt_dt <= SYSDATE -7) OR mo.allow_timesheet_fl = 'N' )
优化关联条件(性能瓶颈核心)
你推测的concat(mo.bld_proj_id,'%') like ud2.udt02_name确实是性能短板:
- 这种反向模糊匹配(判断
ud2.udt02_name是否以mo.bld_proj_id开头)会让数据库无法使用ud2.udt02_name上的索引,只能全表扫描,数据量大时耗时极长。 - 改用Oracle原生字符串拼接符
||,写法更规范,同时如果ud2.udt02_name有前缀索引,有机会命中索引:
ON ud2.udt02_name LIKE mo.bld_proj_id || '%'
改写删除语句,提升执行效率
原SQL用IN子查询的方式,在数据量大时性能不如EXISTS或直接JOIN,推荐两种改写方式:
方式1:用EXISTS替代IN
DELETE FROM tc_0002.charge_branch_charge@etime ctree WHERE EXISTS ( SELECT 1 FROM tc_0002.udt02@etime ud2 INNER JOIN deltek.mo_hdr mo ON ud2.udt02_name LIKE mo.bld_proj_id || '%' WHERE ud2.udt02_id = ctree.charge_cd AND ( (mo.s_mo_status_cd IN ('L','C') AND mo.complt_dt <= SYSDATE -7) OR mo.allow_timesheet_fl = 'N' ) );
方式2:用JOIN直接关联删除(Oracle 12c+支持)
DELETE ctree FROM tc_0002.charge_branch_charge@etime ctree JOIN tc_0002.udt02@etime ud2 ON ud2.udt02_id = ctree.charge_cd JOIN deltek.mo_hdr mo ON ud2.udt02_name LIKE mo.bld_proj_id || '%' WHERE ( (mo.s_mo_status_cd IN ('L','C') AND mo.complt_dt <= SYSDATE -7) OR mo.allow_timesheet_fl = 'N' );
添加必要索引,彻底解决慢查询
给以下字段创建索引,让数据库快速定位关联数据:
- 给
udt02表建组合索引,覆盖关联和查询字段:
CREATE INDEX idx_udt02_name_id ON tc_0002.udt02(udt02_name, udt02_id);
- 给
mo_hdr表建组合索引,覆盖过滤和关联字段:
CREATE INDEX idx_mo_bld_status_dt_fl ON deltek.mo_hdr(bld_proj_id, s_mo_status_cd, complt_dt, allow_timesheet_fl);
- 确保
charge_branch_charge表的charge_cd字段有索引(如果是主键则无需额外创建)。
可选:分批删除避免资源占用
如果待删记录数量巨大,一次性删除会消耗大量undo日志,导致执行缓慢,可采用分批删除的方式:
DECLARE v_rows_deleted NUMBER := 1; BEGIN WHILE v_rows_deleted > 0 LOOP DELETE FROM tc_0002.charge_branch_charge@etime ctree WHERE EXISTS ( SELECT 1 FROM tc_0002.udt02@etime ud2 INNER JOIN deltek.mo_hdr mo ON ud2.udt02_name LIKE mo.bld_proj_id || '%' WHERE ud2.udt02_id = ctree.charge_cd AND ( (mo.s_mo_status_cd IN ('L','C') AND mo.complt_dt <= SYSDATE -7) OR mo.allow_timesheet_fl = 'N' ) ) AND ROWNUM <= 1000; -- 每次删除1000条,可根据实际调整 v_rows_deleted := SQL%ROWCOUNT; COMMIT; END LOOP; END; /
内容的提问来源于stack exchange,提问作者Mimi9864
相关产品推荐
相关产品推荐

