玖叶教程网

前端编程开发入门

SQL优化案例(2):OR条件优化

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

在MySQL中,同样的查询条件,如果变换OR在SQL语句中的位置,那么查询的结果也会有差异,在多个复杂的情况下,可能会带来索引选择不佳的性能隐患,为了避免执行效率大幅度下降的问题,我们可以适当考虑使用Union all对查询逻辑复杂的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.待优化场景



执行计划

2.场景解析

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

3.场景优化

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

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

再回到原始SQL进行改写。



可以抛光优化后的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条件

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

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言