【问题标题】:Using OR on WHERE statement in MySql causes slow excecution在 MySql 中的 WHERE 语句上使用 OR 会导致执行缓慢
【发布时间】:2022-01-19 00:04:03
【问题描述】:

与使用两个单独的查询实现相同的查询相比,以下查询的执行时间几乎是 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

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

【问题讨论】:

    标签: mysql query-optimization


    【解决方案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 查询计划器(还)没有这种逻辑。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多