接下来上一篇文章《 SQL优化案例(1):隐式转换》的介绍,此处内容围绕OR的优化展开。

在MySQL中,同样的查询条件,如果变换OR在SQL语句中的位置,那么查询的结果也会有差异,在多个复杂的情况下,可能会带来索引选择不佳的性能隐患,为了避免执行效率大幅度下降的问题,我们可以适当考虑使用统一所有对查询逻辑复杂的SQL进行分离。

常见OR使用场景,请阅读以下案例。

案例一:不同列使用OR条件查询

1.待优化场景

SELECT
..
..
FROM`t1` a
WHERE a.token= '16149684'
AND a.store_id= '242950'
AND(a.registrationId IS NOT NULL
AND a.registrationId<> '')
OR a.uid= 308475
AND a.registrationId IS NOT NULL
AND a.registrationId<> ''

执行计划

+--------------+-----------------------+-----------------+----------------+-------------------+-------------------+---------------+----------------+---------------------------------------------+
| id | select_type | table | type | key | key_len | ref | rows | Extra |
+--------------+-----------------------+-----------------+----------------+-------------------+-------------------+---------------+----------------+---------------------------------------------+
| 1 | SIMPLE | a | range |idx_registrationid | 99 | | 100445 | Using index condition; Using where |
+--------------+-----------------------+-----------------+----------------+-------------------+-------------------+---------------+----------------+---------------------------------------------+

共返回1行记录,花费 5 ms 。

2.场景解析

从查询条件中可以研磨令牌和uid过滤性都非常好,但是由于使用了,或者,需要采用索引合并的方法才能获得比较好的性能。但在实际执行过程中MySQL优化器替代选择了使用registrationId的索引,导致SQL的性能很差。

3.场景优化

我们将SQL改写成union all的形式。

SELECT
...
...
FROM`t1` a
WHERE a.token = '16054473'
AND a.store_id = '138343'
AND b.is_refund = 1
AND (a.registrationId IS NOT NULL
AND a.registrationId <> '')
union all
SELECT
...
...
FROM`t1` a
where a.uid = 181579
AND a.registrationId IS NOT NULL
AND a.registrationId <> ''
+--------------+-----------------------+-----------------+----------------+------------------------------+---------------+-------------------+------------------------------+----------------+------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+--------------+-----------------------+-----------------+----------------+------------------------------+---------------+-------------------+------------------------------+----------------+------------------------------------+
| 1 | PRIMARY | a | ref | IDX_TOKEN,IDX_STORE_ID_TOKEN | IDX_TOKEN | 63 | const | 1 | Using index condition; Using where |
| 1 | PRIMARY | b | eq_ref | PRIMARY | PRIMARY | 4 | youdian_life_sewsq.a.role_id | 1 | Using where |
| 2 | UNION | a | const | PRIMARY | PRIMARY | 4 | const | 1 | |
| 2 | UNION | b | const | PRIMARY | PRIMARY | 4 | const | 0 | unique row not found |
| | UNION RESULT | <union1,2> | ALL | | | | | | Using temporary |
+--------------+-----------------------+-----------------+----------------+------------------------------+---------------+-------------------+------------------------------+----------------+------------------------------------+

共返回5行记录,花费 5 ms 。

通过对比优化前后的执行计划,可以明显修剪,将SQL合并成两个子查询,再使用union对结果进行合并,稳定性和安全性更好,性能更高。

案例二:同一列使用OR查询条件

1.待优化场景

select
....
....
from
t1 as mci
left join t1 as ccv2_1 on ccv2_1.unique_no = mci=category_no1
left join t1 as ccv2_2 on ccv2_2.unique_no = mci=category_no2
left join t1 as ccv2_3 on ccv2_3.unique_no = mci=category_no3
left join(
select product_id,
count(0) count
from t2 pprod
inner join t3 pinfo on pinfo.promotion_id = pprod.promotion_id
and pprod.is_enable =1
and ppinfo.is_enable=1
and pinfo.belong_t0 =1
and pinfo.end_time >=now()
and not (
pinfo.onshelv_time>'2019-06-30 00:00:00'
or pinfo.end_time>'2018-12-05 00:00:00'
)group by pprod.product_id
)as pc on pc.product_id = mci.product_id
where mci.is_enable =0
and mci.comodifty_type in ('1', '5', '6')
and (pc.count =0 or pc.count isnull ) limit 0,5;

执行计划

2.场景解析

本例的SQL查询中有一个子查询,子查询被当成成驱动表,产生了auto_key,通过SQL进行进行测试,验证主要是(pc.count = 0或pc.count为null)会影响到整个SQL的性能,需要进行比较改写。

3.场景优化

首先我们可以单独思考(pc.count = 0或pc.count为null)如何进行优化?先写一个类似的SQL

Select col from test where col =100 or col is null;
+--------+
| col |
+--------+
| 100 |
| NULL |
+--------+
2 rows in set (0.00 sec)

这个时候我们看到的其实是同一个列,但对应不同的值,这种情况可以利用case when进行转换。

Select col From test where case when col is null then 100 else col =100 end;
+--------+
| col |
+--------+
| 100 |
| NULL |
+--------+
2 rows in set (0.00 sec)

再回到原始SQL进行改写。

select
....
....
from
t1 as mci
left join t1 as ccv2_1 on ccv2_1.unique_no = mci=category_no1
left join t1 as ccv2_2 on ccv2_2.unique_no = mci=category_no2
left join t1 as ccv2_3 on ccv2_3.unique_no = mci=category_no3
left join(
select product_id,
count(0) count
from t2 pprod
inner join t3 pinfo on pinfo.promotion_id = pprod.promotion_id
and pprod.is_enable =1
and ppinfo.is_enable=1
and pinfo.belong_t0 =1
and pinfo.end_time >=now()
and not (
pinfo.onshelv_time>'2019-06-30 00:00:00'
or pinfo.end_time>'2018-12-05 00:00:00'
)group by pprod.product_id
)as pc on pc.product_id = mci.product_id
where mci.is_enable =0
and mci.comodifty_type in ('1', '5', '6')
and case when pc.count is null then 0 else pc.count end=0 limit 0,5;

可以抛光优化后的SQL比原始SQL快了30秒,执行效率提升约50倍。

案例三:优化关联SQL OR条件

1.待优化场景

SELECT user_msg.msg_id AS ‘msg_id’, user_msg.content AS ‘msg_content’, …
FROM user_msg
LEFT JOIN user ON user_msg.user_id = user.user_id
LEFT JOIN group ON user_msg.group_id = group.group_id
WHERE user_msg.gmt_modified >= date_sub('2018-03-29 09:31:44', INTERVAL30SECOND)
OR user.gmt_modified >= date_sub('2018-03-29 09:31:44', INTERVAL 30 SECOND)
OR group.gmt_modified >= date_sub('2018-03-29 09:31:44', INTERVAL 30 SECOND)

2.场景解析

我们仔细分析上述查询语句,发现虽然业务逻辑只需要查询半分钟内修改的数据,但执行过程却必须对所有的数据进行关联操作,带来的性能损失。

3.场景优化

我们对原始SQL进行分解操作,第一部分sql-01如下:

SELECT user_msg.msg_id AS ‘msg_id’, user_msg.content AS ‘msg_content’, …
FROM user_msg
LEFT JOIN user ON user_msg.user_id = user.user_id
LEFT JOIN group ON user_msg.group_id = group.group_id
WHERE user_msg.gmt_modified >= date_sub('2018-03-29 09:31:44', INTERVAL 30 SECOND)

sql-01以user_msg表为驱动,使用gmt_modified索引过滤最新数据。

第二部分sql-02如下:

SELECT user_msg.msg_id AS ‘msg_id’, user_msg.content AS ‘msg_content’, …
FROM user_msg
LEFT JOIN user ON user_msg.user_id = user.user_id
LEFT JOIN group ON user_msg.group_id = group.group_id
WHERE user.gmt_modified >= date_sub('2018-03-29 09:31:44', INTERVAL 30 SECOND)

sql-02以用户为驱动表,msg user_id的索引过滤行很好。

第三部分sql-03如下:

SELECT user_msg.msg_id AS ‘msg_id’, user_msg.content AS ‘msg_content’, …
FROM user_msg
LEFT JOIN user ON user_msg.user_id = user.user_id
LEFT JOIN group ON user_msg.group_id = group.group_id
WHERE group.gmt_modified >= date_sub('2018-03-29 09:31:44', INTERVAL 30 SECOND)

sql-03以group为驱动表,使用gmt_modified索引过滤最新数据。

总结

MySQL OR条件优化的常见场景主要有以下情况:

1,相同列可以使用IN进行代替

2,不同列及复杂的情况下,可以使用union all进行分离

3,关联SQL OR条件

我们需要结合实际场景,分析优化。

更多技术可以去官网查看https://www.dtstack.com/dtsmart/

SQL优化案例(2):OR条件优化的更多相关文章

  1. 优化案例--改写IN条件为INNER JOIN

    --====================================== --原始语句 SET STATISTICS IO ON SELECT COUNT(DISTINCT parent_co ...

  2. MySQL参数优化案例

    环境介绍 优化层级与指导思想 优化过程 最小化安装情况下的性能表现 优化innodb_buffer_pool_size 优化innodb_log_files_in_group&innodb_l ...

  3. SQL性能优化案例分析

    这段时间做一个SQL性能优化的案例分析, 整理了一下过往的案例,发现一个比较有意思的,拿出来给大家分享. 这个项目是我在项目开展2期的时候才加入的, 之前一期是个金融内部信息门户, 里面有个功能是收集 ...

  4. SQL 优化案例 1

    create or replace procedure SP_GET_NEWEST_CAPTCHA( v_ACCOUNT_ID in VARCHAR2, --接收短信的手机号 v_Tail_num i ...

  5. sqlserver sql优化案例及思路

    始sql: SELECT TOP 100 PERCENT ZZ.CREW_NAME AS 机组, ZZ.CREW_ID, AA.年度时间, CC.当月时间, DD.连续七天时间 AS 最近七天 FRO ...

  6. SQL 优化案例

    create or replace procedure SP_GET_NEWEST_CAPTCHA( v_ACCOUNT_ID in VARCHAR2, --接收短信的手机号 v_Tail_num i ...

  7. 百倍性能的PL/SQL优化案例(r11笔记第13天)

    我相信你是被百倍性能的字样吸引了,不过我所想侧重的是优化的思路,这个比优化技巧更重要,而结果嘛,其实我不希望说成是百倍提升,“”自黑“”一下. 有一个真实想法和大家讨论一下,就是一个SQL语句如果原本 ...

  8. Hive优化案例

    1.Hadoop计算框架的特点 数据量大不是问题,数据倾斜是个问题. jobs数比较多的作业效率相对比较低,比如即使有几百万的表,如果多次关联多次汇总,产生十几个jobs,耗时很长.原因是map re ...

  9. (4.13)SQL Server profile使用、数据库优化引擎顾问使用

    SQL Server profile使用技巧 介绍 经常会有人问profile工具该怎么使用?有没有方法获取性能差的sql的问题.自从转mysql我自己也差不多2年没有使用profile,忽然prof ...

  10. MySQL的索引单表优化案例分析

    建表 建立本次优化案例中所需的数据库及数据表 CREATE DATABASE db0206; USE db0206; CREATE TABLE `db0206`.`article`( `id` INT ...

随机推荐

  1. 「loj - 6179」Pyh 的求和

    link. 我们想要求出 \(\varphi(ij)=\varphi(i)\varphi(j)C\) 中的常数.先研究 \(i=p^a\),\(j=p^b\) 的情况,即 \(\varphi(p^{a ...

  2. C#开源且免费的Windows桌面快速预览神器 - QuickLook

    前言 今天给大家推荐一款由C#开源且免费的Windows桌面快速预览神器:QuickLook. 工具介绍 QuickLook是一款在Windows操作系统上的实用工具,它提供了一种快速预览文件内容的方 ...

  3. 判断两个数a,b,输出较大数的平方值。所谓平方值就是两个相同的数相乘的积。

    平方值   描述 判断两个数a,b,输出较大数的平方值.所谓平方值就是两个相同的数相乘的积. 输入 两个数a和b 输出 输出较大数的平方值. 输入样例 1 1 2 输出样例 1 4 a,c = map ...

  4. Aoba's GitLab Doki Theme - 一个简单的 GitLab 主题工具

    前言 平常工作在用 GitLab 但总觉得缺点什么颜色好单调,于是随手摸了一个主题工具 界面预览 GitLab 主页效果 个人偏好配置页面 安装方法 安装 Tampermonkey 之类的用户脚本工具 ...

  5. 线段树(nb)

    今天刚学习了线段树,赶紧趁热打了两遍模版 下面都是线段树的基本操作,这个板子是维护的区间中的最大值,当然修改change和build包括线段树中的data可以维护区间上的不同信息. 首先介绍一下线段树 ...

  6. JVM SandBox 的技术原理与应用分析

    https://www.infoq.cn/article/tsy4lgjvsfweuxebw*gp https://blog.csdn.net/qq_40378034/article/details/ ...

  7. 论文精读:用于少样本目标检测的元调整损失函数和数据增强(Meta-tuning Loss Functions and Data Augmentation for Few-shot Object Detection)

    论文链接:Meta-Tuning Loss Functions and Data Augmentation for Few-Shot Object Detection Abstract 现阶段的少样本 ...

  8. vivado生成Bitstream报错[Vivado 12-1345] Error(s) found during DRC. Bitgen not run(Vivado 2017.4)。

    写了一个很简单的程序,2-4译码器. module decoder2to4( input in1, in0, output reg [3:0]out ); always @ (*) begin if ...

  9. 腾讯云与树莓派通信遇到的一些问题(树莓派无法ping通腾讯云指定端口)

    采用的是socket通信,之前写过C的,这次需要用到python,参考的代码原链接如下:https://www.cnblogs.com/mosu/p/16072146.html. (1)首先开放一个端 ...

  10. EventBus 简明教程

    简介 EventBus 是一个用于 Android 和 Java 编程的 事件发布/订阅框架.使用 EventBus 进行事件传递,事件的发布和订阅就被充分解耦合,这使得编程人员从传统而原始的事件传递 ...