暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

技术分享 | 又是一条慢 SQL 改写,拿捏!

155

作者:马文斌

MySQL 爱好者。

本文来源:原创投稿

* 爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。


1背景

开发同学丢了一条 SQL 过来。“马哥,看看这条 SQL 能否优化,业务那边反馈很慢!”看了下执行计划+表结构,索引都没问题。那到底是怎么回事呢?咱们一起来瞧瞧。

2分析原 SQL

explain SELECT
        count(0)
FROM
        invoice_sales_application a
WHERE
        (
                shop_order_id LIKE '23060919546335%'
                OR (
                        EXISTS (
                                SELECT
                                        1
                                FROM
                                        invoice_sales_application_detail b
                                WHERE
                                        a.application_no = b.application_no
                                AND a.invoice_category = b.invoice_category
                                AND b.del_flag = 0
                                AND b.shop_order_id LIKE '23060919546335%'
                        )
                        AND a.is_merge = 1
                )
        )

先来看看这个 SQL 是什么意思:

invoice_sales_application
表中,shop_order_id
'23060919546335%'
开头,或者存在一个相关的 invoice_sales_application_detail
表中的记录,该记录的 application_no
invoice_category
invoice_sales_application
表中的相同,并且 shop_order_id
'23060919546335%'
开头,同时 invoice_sales_application
表中的 is_merge
字段为 1

执行计划:all+ref,其中 a
表要扫描 116w 行的数据。

执行计划

执行需要 43s,且有一个全表扫描。

扫描时间

3优化操作

EXISTS 转化成 JOIN 的方式

这里是把 EXISTS
改写成 INNER JOIN
通过索引关键关联,应该会有不错的效果,试试看。

SELECT  count(0)
FROM invoice_sales_application a INNER
JOIN invoice_sales_application_detail b
  ON a.application_no = b.application_no
WHERE ( a.shop_order_id LIKE '23060919546335%'
    OR ( b.shop_order_id LIKE '23060919546335%'
    AND a.is_merge = 1 ) )
    AND a.invoice_category = b.invoice_category
    AND b.del_flag = 0

执行效果

这里虽然转化了 INNER JOIN
的方式,执行计划还是 all+ref ,因为用了 OR
导致 a
表没有用上索引,还是用的全表扫描。没关系,咱们再次进行转化。

OR 改成 UNION

 SELECT count(*)
 FROM invoice_sales_application a
 INNER JOIN invoice_sales_application_detail b ON a.application_no = b.application_no
 AND a.invoice_category = b.invoice_category
 AND b.del_flag = 0
 WHERE a.shop_order_id = '23060919546335'
 AND a.del_flag = 0
 UNION
 SELECT count(*)
 FROM invoice_sales_application a
 INNER JOIN invoice_sales_application_detail b ON a.application_no = b.application_no
 AND a.invoice_category = b.invoice_category
 AND b.del_flag = 0
 WHERE b.shop_order_id = '23060919546335'
 AND a.is_merge = 1
 AND a.del_flag = 0;

在看看执行计划,eq_ref+ref+ref+ref。说明已经优化的很好了,起码没有全表扫描。

执行计划

最后看看结果。

执行计划

这样 SQL 执行很快了,查询时间从 42s 降到 18ms,快了几个数量级。

4小结

1、当 SQL 的主架构中含有 EXISTS
的时候,可以改成 INNER JOIN
的方式,先看看效果。

2、当条件中有 OR
的时候,可以改成 UNION
试试。


本文关键字:#MySQL# #SQL优化#

文章转载自杨建荣的学习笔记,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论