【问题标题】:In MySQL, how can I make best use of index when using or condition across two fields in different tables?在 MySQL 中,如何在不同表中的两个字段上使用或条件时充分利用索引?
【发布时间】:2019-07-29 13:43:38
【问题描述】:
select
    tableA.field1,
    tableB.field2
from
    tableA
    inner join tableB on table1.someField = table2.someField
where
    tableA.field1 = 'value1'
    or tableB.field2 = 'value2'

我在 tableA.field1 和 tableB.field2 上都有很好的索引。如果我将 where 子句从“or”更改为“and”,那么查询速度很快。但是,当使用 or 时,它似乎只能使用两个索引之一。

两个表都包含数 100,000 条记录。

在 where 子句中有 or 时,是否需要重新设计查询以利用两个索引?

【问题讨论】:

标签: mysql


【解决方案1】:

我会重新编写查询以组合两个单独的集合,避免WHERE 子句中的OR

类似这样的:

( SELECT a1.field1
       , b1.field2
    FROM tableA a1
    JOIN tableB b1
      ON b1.somefield = a1.somefield
   WHERE a1.field1 = 'value1'
)   
UNION ALL
( SELECT a2.field1
       , b2.field2
    FROM tableA a2
    JOIN tableB b2
      ON b2.somefield = a2.somefield
   WHERE b2.field2 = 'value2'
  HAVING NOT (a2.field1 <=> 'value1')
)

该查询应该能够有效地利用这些索引,用于 SELECT

 ... ON tableA (field1,somefield,...)
 ... ON tableB (somefield,field2,...)

第二次选择

 ... ON tableA (somefield,field1,...)
 ... ON tableB (field2,somefield,...)

使用 EXPLAIN 查看执行计划


查看返回集合的另一种方法是三个集合的组合...

  • 在 a.field1 和 b.field2 上都匹配的行
  • 在 a.field1 上匹配但在 b.field2 上不匹配的行
  • 在 b.field2 上匹配但在 a.field1 上不匹配的行

第一个子集是原始查询,将 OR 替换为 AND

最后两个子集都可以返回

SELECT ...
  FROM a
  JOIN b
    ON ...
 WHERE a.field1 = 'value1'
   AND NOT ( b.field2 <=> 'value2' )

SELECT ...
  FROM a
  JOIN b
    ON ...
 WHERE b.field2 = 'value2'
   AND NOT ( a.field1 <=> 'value1' )

为每个 SELECT 提供合适的索引,并使用 UNION ALL 集合运算符组合集合。

【讨论】:

  • 谢谢。我得出的结论是我需要一个工会,但不太确定,很高兴知道你有相同的意见。我认为我不需要在第二个查询中使用 HAVING 子句。我也不知道 运算符,这很好知道,但我们在这里需要它吗?我们也不能只使用 UNION 而不是 UNION ALL 吗?
  • UNION 集合运算符删除重复值; UNION ALL 集合运算符没有。因此,就从原始查询复制结果而言,UNION ALL 会这样做; UNION 也可能会这样做,但这将是一个更特殊的情况,而不是一般情况。从性能的角度来看,UNION 将导致“使用文件排序”操作。是使用UNION ALL 还是UNION 真的取决于规范。在满足规范的情况下,UNION ALL 将提供更好的性能。
  • 第二个 SELECT 的 HAVING 子句中的条件是为了避免返回第一个 SELECT 已经返回的相同行。我们可以重写HAVING (a.field1 &lt;&gt; 'value1' OR a.field1 IS NULL)。在我看来,NOT (a.field1 &lt;=&gt; 'value1') 只是一种更直接的否定a.field1 = 'value1 的方法(在一般情况下,我们不能保证field1 是非NULL。如果我们有某种保证,那么field1是非NULL(我们在问题中看不到任何证据),那么我们可以做a.field1 &lt;&gt; 'value1'
猜你喜欢
  • 2019-02-24
  • 2017-04-11
  • 2012-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多