【问题标题】:Query takes too long to query with OR clause but their parts are very quick使用 OR 子句查询需要很长时间,但它们的部分非常快
【发布时间】:2018-08-27 16:26:22
【问题描述】:

我有两个表,它们的 Id 索引了大约 100 万行。

闲置查询...

SELECT t.* FROM transactions t
INNER JOIN integration it ON it.id_trans = t.id_trans
WHERE t.id_trans = '5440073'
OR it.id_integration = '439580587'

此查询大约需要 30 秒。但是……

SELECT ... WHERE t.id_trans = '5440073'

花费不到 100 毫秒并且

SELECT ... WHERE it.id_integration = '439580587'

也需要不到 100 毫秒。甚至

SELECT ... WHERE t.id_trans = '5440073'
UNION
SELECT ... WHERE it.id_integration = '439580587'

不到 100 毫秒

为什么 OR 子句即使零件速度如此之快也要花费这么多时间?

【问题讨论】:

  • “我应该怎么做才能提高这个查询的性能?” 你已经做到了;将此类有问题的 OR 条件拆分为连接查询是处理这种情况的标准方法之一。问题是因为 MySQL 不能在一个(非 UNIONed)查询中同时使用两个索引。
  • 有趣,Uueerdo。这是 MySQL 特有的吗?
  • 我只是在 MySQL 上非常熟悉它,但快速谷歌搜索“MSSQL OR Performance”表明这个问题更为普遍。可能是由于实现优化与效果;抽象地说,WHERE 应该在FROM 中的所有内容之后发生,但是为了使事情变得更快,大多数 SQL 实现会通过首先使用 where 条件来尽早过滤它们。但是,在 OR 的情况下,您不能先应用两者,因为这实际上是 AND。它可以尝试从两个方向开始并有效地执行标准的 UNION,但这可能说起来容易做起来难。

标签: mysql sql database performance


【解决方案1】:

为什么OR 这么慢,而UNION 这么快?

你明白为什么UNION 很快吗?因为它可以很好地使用两个独立的索引,并从UNION的每个部分收集一些结果行,然后将结果组合在一起。

但是为什么OR 不能这样做呢?简而言之,优化器不够聪明,无法尝试那个角度。

在您的情况下,测试在不同的表上;这导致UNION 的两个部分的查询计划完全不同(参见EXPLAIN SELECT ...)。每个都可以很好地优化,所以每个都很快。

假设每个部分只交付几行,UNION 的后续开销很小——即收集两组小行,对它们进行重复数据删除(如果您使用UNION DISTINCT 而不是UNION ALL),以及交付结果。

同时,OR 查询有效地收集了两个表的所有组合,然后根据OR 的两个部分过滤掉。中间阶段可能涉及到一个巨大的临时表,只是为了扔掉大部分行。

(inflate-deflate 的另一个例子是JOINs + GROUP BY。解决方法不同。)

【讨论】:

    【解决方案2】:

    我建议使用UNION ALL 编写查询:

    SELECT t.*
    FROM transactions t
    WHERE t.id_trans = '5440073'
    UNION ALL
    SELECT t.*
    FROM transactions t JOIN
         integration it 
         ON it.id_trans = t.id_trans
    WHERE t.id_trans <> '5440073' AND
          it.id_integration = '439580587';
    

    注意:如果 id 确实是数字(而不是字符串),则去掉单引号。混合类型有时会混淆优化器并阻止索引的使用。

    【讨论】:

    • 我仍然没有找到为什么 OR 子句需要这么长时间才能完成
    • OR 经常阻止使用索引。
    • @RickJames。 . .咳咳,t.id_trans &lt;&gt; '5440073' 在第二个子查询中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-02
    • 2018-01-09
    • 2015-05-17
    相关资源
    最近更新 更多