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

梁敬彬梁敬弘兄弟出品

往期回顾

SQL等价改写优化案例精选第一部【一统天下】① count(case when)的合并魔力
SQL等价改写优化案例精选第一部【一统天下】②分析函数的穿透之力
SQL等价改写优化案例精选第一部【一统天下】③ group by (case when)的聚合艺术

SQL优化路上,我们常能见到通过UNION/UNION ALL组合多个相似查询的写法,这种方式虽然清晰易懂,但对于那些查询同一表且条件相似的情况,使用OR条件替代往往能带来显著的性能提升。本案例将展示如何识别并优化这类场景。

在这里插入图片描述

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

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('2010-08-02', 'YYYY-MM-DD')
   and log_time <  TO_DATE('2010-08-03', 'YYYY-MM-DD') + 1
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('2010-08-02', 'YYYY-MM-DD')
          and log_time <  TO_DATE('2010-08-03', 'YYYY-MM-DD') + 1)
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_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('2010-08-02', 'YYYY-MM-DD')
          and log_time <  TO_DATE('2010-08-03', 'YYYY-MM-DD') + 1)
union 
  (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('2010-08-02', 'YYYY-MM-DD')
          and log_time <  TO_DATE('2010-08-03', 'YYYY-MM-DD') + 1)
很明显,此处的UNION ALL 完全可以用OR 来改造,等价改写,减少表扫描次数。优化改造后SQL为:
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('2010-08-02', 'YYYY-MM-DD')
   and log_time < TO_DATE('2010-08-03', 'YYYY-MM-DD') + 1 
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失败%') 
        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('2010-08-02', 'YYYY-MM-DD')
          and log_time <  TO_DATE('2010-08-03', 'YYYY-MM-DD') + 1

分析问题

仔细观察上述SQL,我发现几个明显的优化机会:

  1. 第一和第四部分都从dcc_sys_log表查询,条件几乎完全相同,只是cause的模糊匹配条件不同
  2. 第二和第三部分都从dcc_ne_log表查询,除了特定条件外,其他过滤条件完全相同
  3. 每个子查询都包含相同的时间范围、deal_log和alert_type条件

这种情况下,我们可以通过合并相同表的查询,使用OR条件替代UNION,减少表扫描次数。

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('2010-08-02', 'YYYY-MM-DD')
   and log_time < TO_DATE('2010-08-03', 'YYYY-MM-DD') + 1 
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失败%') 
        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('2010-08-02', 'YYYY-MM-DD')
          and log_time <  TO_DATE('2010-08-03', 'YYYY-MM-DD') + 1

优化效果分析

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

  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等价改写优化案例精选第二部【精兵简政】① 莫名其妙的行列转换

系列回顾

“大白话人工智能” 系列
“数据库拍案惊奇” 系列
“世事洞明皆学问” 系列

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

收获不止数据库

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值