在 MySql 中的 WHERE 语句上使用 OR 会导致执行缓慢

Posted

技术标签:

【中文标题】在 MySql 中的 WHERE 语句上使用 OR 会导致执行缓慢【英文标题】:Using OR on WHERE statement in MySql causes slow excecution 【发布时间】:2016-10-11 09:05:37 【问题描述】:

与使用两个单独的查询实现相同的查询相比,以下查询的执行时间几乎是 mysql 的 7 倍,并且避免了 WHERE 语句上的 OR。我更喜欢使用单个查询,因为我可以对所有内容进行排序和分组。

这是有问题的查询:

EXPLAIN SELECT  *
    FROM  `posts`
    LEFT JOIN  `teams_users`
               ON (teams_users.team_id=posts.team_id
              AND  teams_users.user_id='7135')
    WHERE  (teams_users.status='1'
              OR  posts.user_id='7135');

结果:

id select_type table type possible_keys key key_len ref rows Extra 
1 SIMPLE  posts       ALL    user_id NULL NULL NULL 169642 
1 SIMPLE  teams_users eq_ref PRIMARY PRIMARY 8 posts.team_id,const 1 Using where

现在,如果我改为执行以下两个查询,则总执行时间将缩短 7 倍:

EXPLAIN SELECT  *
    FROM  `posts`
    LEFT JOIN  `teams_users`
               ON (teams_users.team_id=posts.team_id
              AND  teams_users.user_id='7135')
    WHERE  (teams_users.status='1');

结果:

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE  teams_users ref PRIMARY,status status 1 const 5822 Using where
1 SIMPLE  posts       ref team_id  team_id 5 teams_users.team_id 9 Using where

和:

EXPLAIN SELECT  *
    FROM  `posts`
    LEFT JOIN  `teams_users`
               ON (teams_users.team_id=posts.team_id
              AND  teams_users.user_id='7135')
    WHERE  (posts.user_id='7135');

结果:

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE  posts       ref    user_id user_id 4 const 142 
1 SIMPLE  teams_users eq_ref PRIMARY PRIMARY 8 posts.team_id,const 1

显然,这两个查询的扫描行数要少得多。 为什么初始查询很慢? 谢谢。

【问题讨论】:

【参考方案1】:

是的,OR 经常是性能杀手。一个常见的解决方法是使用UNION。以您为例:

SELECT  *
    FROM  `posts`
    LEFT JOIN  `teams_users`
               ON (teams_users.team_id=posts.team_id
              AND  teams_users.user_id='7135')
    WHERE  (teams_users.status='1')
UNION DISTINCT
SELECT  *
    FROM  `posts`
    LEFT JOIN  `teams_users`
               ON (teams_users.team_id=posts.team_id
              AND  teams_users.user_id='7135')
    WHERE  (posts.user_id='7135');

如果您确定没有重复,请更改为更快的UNION ALL

如果您不是在寻找缺少的team_users 行,请使用JOIN 而不是LEFT JOIN

如果您需要ORDER BY,请添加一些括号:

( SELECT ... )
UNION ...
( SELECT ... )
ORDER BY ...

否则,ORDER BY 将仅适用于第二个SELECT。 (如果您还需要“分页”,请参阅my blog。)

请注意,在某些情况下您可能还需要LIMIT

【讨论】:

谢谢。事实上,UNION 比在 When 语句上使用 OR 快得多。【参考方案2】:

没有OR 子句的查询都是sargable. That is, they both can be satisfied using indexes。

如果 MySQL 查询计划器包含逻辑以计算出它可以将其重写为两个查询的 UNION ALL,那么带有 OR 的查询将是 sargable。 MySQL 查询计划器(还)没有这种逻辑。

因此,它会进行表扫描以获取结果集。这些通常非常缓慢。

【讨论】:

以上是关于在 MySql 中的 WHERE 语句上使用 OR 会导致执行缓慢的主要内容,如果未能解决你的问题,请参考以下文章

Python 操作Redis

python爬虫入门----- 阿里巴巴供应商爬虫

Python词典设置默认值小技巧

《python学习手册(第4版)》pdf

Django settings.py 的media路径设置

Python中的赋值,浅拷贝和深拷贝的区别