ITPub博客

首页 > 数据库 > 数据库开发技术 > 基于达梦的SQL等价改写优化案例精选第一部【一统天下】④ or 与 union all的优化交响

基于达梦的SQL等价改写优化案例精选第一部【一统天下】④ or 与 union all的优化交响

原创 数据库开发技术 作者:梁敬彬 时间:2025-05-21 08:35:09 0 删除 编辑


往期回顾

  SQL等价改写优化案例精选第一部【一统天下】① count(case when)的合并魔力

 SQL等价改写优化案例精选第一部【一统天下】②分析函数的穿透之力 

  SQL等价改写优化案例精选第一部【一统天下】③ group by (case when)的聚合艺术


在SQL开发中,我们经常会遇到这样的情况:需要组合多个相似但略有不同的查询结果。大多数开发者本能地使用UNION/UNION ALL来解决,这种方式直观易懂,但在特定场景下却隐藏着巨大的性能浪费。

本案例将从执行原理层面,深入剖析为什么在某些情况下,OR条件能比UNION ALL带来质的性能提升。

案例4 寻找用or替代union all的时机

-- 第一部分:查询dcc_sys_log表中cause条件为"对端被关闭%"的记录
select peer_id    对端标识,
       null       源域名,
       null       目标域名,
       alert_type 告警类型,
       log_time   告警时间,
       cause      告警内容,
       deal_log   处理状态,
       deal_staff 处理人,
       deal_time  处理时间,
       remark     备注
  from dcc_sys_log
 where action = 'disconnect'
   and cause like '对端被关闭%'
   and deal_log = 'deal_log'
   and alert_type = 'alert_type'
   and log_time >= TO_DATE('2024-08-02', 'YYYY-MM-DD')
   and log_time <  TO_DATE('2024-08-03', 'YYYY-MM-DD') + 1
-- 第二部分:查询dcc_ne_log表中result=0的记录
union 
  (select  peer_id         对端标识,
              origin_host  源域名,
              dest_host   目标域名,
              alert_type   告警类型,
              log_time    告警时间,
              cause       告警内容,
              deal_log    处理状态,
              deal_staff   处理人,
              deal_time   处理时间,
              remark      备注
         from dcc_ne_log
        where result = 0
          and cause like 'parser失败%'
          and deal_log = 'deal_log'
          and alert_type = 'alert_type'
          and log_time >= TO_DATE('2024-08-02', 'YYYY-MM-DD')
          and log_time <  TO_DATE('2024-08-03', 'YYYY-MM-DD') + 1)
union 
-- 第三部分:查询dcc_ne_log表中result_code特定值的记录
  (select  peer_id         对端标识,
              origin_host 源域名,
              dest_host   目标域名,
              alert_type   告警类型,
              log_time    告警时间,
              cause       告警内容,
              deal_log     处理状态,
              deal_staff    处理人,
              deal_time    处理时间,
              remark      备注
         from dcc_ne_log
        where result_code = 'DIAMETER_UNABLE_TO_DELIVER'
          and svcctx_id like 'SR-Timeout%'
          and deal_log = 'deal_log'
          and alert_type = 'alert_type'
          and log_time >= TO_DATE('2024-08-02', 'YYYY-MM-DD')
          and log_time <  TO_DATE('2024-08-03', 'YYYY-MM-DD') + 1)
union 
-- 第四部分:查询dcc_sys_log表中cause条件为"接收消息异常%"的记录
  (select peer_id    对端标识,
              null       源域名,
              null       目标域名,
              alert_type  告警类型,
              log_time   告警时间,
              cause      告警内容,
              deal_log   处理状态,
              deal_staff  处理人,
              deal_time  处理时间,
              remark     备注
         from dcc_sys_log
        where action = 'disconnect'
          and cause like '接收消息异常%'
          and deal_log = 'deal_log'
          and alert_type = 'alert_type'
          and log_time >= TO_DATE('2024-08-02', 'YYYY-MM-DD')
          and log_time <  TO_DATE('2024-08-03', 'YYYY-MM-DD') + 1)


分析问题

这段SQL的执行过程实际上是:

1. 四次独立的表扫描:

- 两次对dcc_sys_log表的完整扫描

- 两次对dcc_ne_log表的完整扫描

2. 四次过滤操作:

- 每个子查询各自应用WHERE条件进行过滤

3. 三次合并操作:

- 对四个结果集执行UNION操作,包含排序和去重

4. 四次重复的公共条件判断:

- deal_log = 'deal_log'

- alert_type = 'alert_type'

- 相同的时间范围条件

这种写法的效率问题主要体现在:相同表被多次无谓地扫描,公共条件被重复判断,导致CPU和I/O资源的严重浪费。

精准优化:拆解和合并

经过分析,我们可以看到明显的优化空间:

1. 相同表的查询合并:

- 第一部分和第四部分都查询dcc_sys_log表

- 第二部分和第三部分都查询dcc_ne_log表

2. 差异点隔离:

- dcc_sys_log表的差异仅在于cause条件

- dcc_ne_log表的差异在于result/result_code/svcctx_id条件组合

将这些相似查询通过OR条件合并后的SQL:

-- 第一部分:合并对dcc_sys_log的两次查询
select peer_id    对端标识,
       null       源域名,
       null       目标域名,
       alert_type 告警类型,
       log_time   告警时间,
       cause      告警内容,
       deal_log    处理状态,
       deal_staff   处理人,
       deal_time  处理时间,
       remark     备注
  from dcc_sys_log
 where action = 'disconnect'
   and (cause like '对端被关闭%' or cause like '接收消息异常%')
   and deal_log = 'deal_log'
   and alert_type = 'alert_type'
   and log_time >= TO_DATE('2024-08-02', 'YYYY-MM-DD')
   and log_time < TO_DATE('2024-08-03', 'YYYY-MM-DD') + 1 
union 
-- 第二部分:合并对dcc_ne_log的两次查询
select peer_id              对端标识,
              origin_host  源域名,
              dest_host   目标域名,
              alert_type   告警类型,
              log_time    告警时间,
              cause       告警内容,
              deal_log     处理状态,
              deal_staff    处理人,
              deal_time    处理时间,
              remark       备注
         from dcc_ne_log
        where ((result = 0  and cause like 'parser失败%')  -- 第一个差异条件组合
        or (result_code = 'DIAMETER_UNABLE_TO_DELIVER' and svcctx_id like 'SR-Timeout%'))  -- 第二个差异条件组合
          and deal_log = 'deal_log'
          and alert_type = 'alert_type'
          and log_time >= TO_DATE('2024-08-02', 'YYYY-MM-DD')
          and log_time <  TO_DATE('2024-08-03', 'YYYY-MM-DD') + 1

实际应用中的注意事项

1. 括号使用:在复杂OR条件组合中,正确使用括号确保逻辑正确

where ((result = 0 and cause like 'parser失败%') 
       or (result_code = 'DIAMETER_UNABLE_TO_DELIVER' and svcctx_id like 'SR-Timeout%'))
  and deal_log = 'deal_log'

2. OR vs AND优先级:OR条件比AND优先级低,必要时使用括号明确优先级

3. 处理NULL值:当OR条件两侧涉及NULL值判断时,需特别注意逻辑正确性

4. 验证结果一致性:务必确认优化前后结果完全一致

5. 索引使用情况:通过执行计划检查OR条件是否影响了索引使用

 优化效果分析

这个优化带来了几个明显的好处:

 1. 表扫描次数减少:原来需要扫描dcc_sys_log表2次,dcc_ne_log表2次,现在各只需扫描1次

 2. I/O减少:减少了50%的表扫描,相应减少了磁盘I/O操作

 3. 代码简化:代码行数从34行减少到24行,更加简洁易读

 4. 排序操作减少:UNION操作需要对结果进行排序去重,合并后的查询能减少排序工作量

小结

本例展示了"一统天下"优化思想中的重要一环:通过OR条件替代UNION操作,减少表扫描次数。这种简单却有效的优化方法,在实际工作中经常被忽视。通过识别SQL中的重复访问模式,将相似条件的查询合并,我们可以在不改变业务逻辑的前提下,显著提升SQL执行效率。

在实际优化工作中,建议首先尝试通过EXPLAIN PLAN分析查询性能,然后再决定是使用OR条件还是保留UNION结构,以获得最佳性能。


未完待续...

SQL等价改写优化案例精选第二部【精兵简政】① 莫名其妙的行列转换


公众号:收获不止数据库

微信:ljbyxl1213

系列回顾




来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/9091385/viewspace-3084876/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
历任中电福富研究院副理事长、中盾安信研究院副院长、宁德时代技术顾问等职务,受聘电信集团专家和国家十四五课题负责人,著有《收获,不止Oracle》《收获,不止SQL优化》等多本畅销数据库技术书籍。

注册时间:2024-02-06

  • 博文量
    162
  • 访问量
    198284