【问题标题】:Using an index in MySQL JOIN with OR condition在带有 OR 条件的 MySQL JOIN 中使用索引
【发布时间】:2012-06-20 21:17:18
【问题描述】:

我正在运行一个看起来像这样的查询

SELECT parent.field, child.field
FROM parent
JOIN child ON (child.id = parent.id 
    OR child.id = parent.otherid)

然而,这确实很慢(大约 100k 条记录,并且在真实版本中连接到其他表),但尽管尝试了索引

parent.id (PRIMARY),  
parent.otherid,  
child.id (PRIMARY), 
and a composite index of parent.id and parent.otherid

在进行此连接时,我无法让 MySQL 使用任何这些索引。

我读到 MySQL 每个连接只能使用一个索引,但是当 JOIN 包含 OR 条件时,它是否可以使用复合索引却找不到任何地方。

这里有人知道是否可以让这个查询引用索引吗? 如果有,怎么做?


我的解决方案

(所以不会让我在 atm 下面回答我自己的问题)

经过一系列调整,提出了一个相当不错的解决方案,它保留了 JOIN 和聚合其他表的能力。

SELECT parent.field, child.field
FROM parent
JOIN (
    SELECT parent.id as parentid, 
    # Prevents the need to union
    IF(NOT ISNULL(parent.otherid) AND parent.otherid <> parent.id, 
       parent.otherid, 
       parent.id) as getdataforid
    FROM parent
    WHERE (condition)
) as foundrecords
    ON foundrecords.parentid = parent.id
JOIN child ON child.id = parent.getdataforid

为了提高速度,需要子查询内部的条件来减少放置在临时表中的记录数,但我在外部查询上有大量额外的连接,一些连接到子查询,一些连接到父查询(有一些聚合)所以这个最适合我。

在许多情况下,联合会更快、更有效,但由于我正在过滤父级,但想要来自子级的额外数据(父级自我引用),联合导致我无法合并的额外行. 只需将 parent 连接到自身并在外部查询中为 where 条件设置别名,就有可能找到相同的结果,但这对我来说非常有效。

感谢 Jirka 的 UNION ALL 建议,这是促使我来到这里的原因 :)

【问题讨论】:

  • 您是否对查询运行了explain select ...
  • 是的,解释是什么让我知道它没有使用任何索引进行此连接。

标签: mysql indexing


【解决方案1】:

从理论上讲,您的查询使得一个孩子有两个不同的父母是可能的,这将使它成为非常不标准的术语。但是,让我们假设您的数据模式使这成为不可能。

然后,以下使用单独的索引为您提供相同的结果,每列一个索引。

SELECT parent.field, child.field
FROM parent
JOIN child ON child.id = parent.id 

UNION ALL

SELECT parent.field, child.field
FROM parent
JOIN child ON child.id = parent.otherid

【讨论】:

  • +1。我建议尝试一下,将其分解为两个单独的查询,然后组合结果,尽管此查询可能返回比原始查询更多的行。 (考虑 id = otherid 的 parent 中的一行)。为了保证相同的结果,请在第二个查询中添加谓词 AND parent.otherid &lt;&gt; parent.id。 (NULL 在该测试中不是问题,因为 parent.id 不是 NULL,因为它是 PRIMARY KEY。)
  • 这是简化情况下的绝佳解决方案(谢谢),但它可以防止子数据聚合(使用 GROUP_CONCAT(DISTINCT child.field))。玩这个想法,看看我是否可以应用它来加速类似的查询,但没有聚合。谢谢:D
  • @BobDavies - 你可以。将其嵌入为子查询。
  • 虽然这个答案(和另一个)假设这是连接模型中的表,但在运行分析查询时也完全有可能遇到这种情况,其中每个值集都在多个上下文中使用,例如汇总,因此除非您可以将它们拆分为专有上下文(例如汇总,这不是您想要的),否则此答案并没有真正的帮助。
  • @bsplosion - 这是真的。随意添加基于 CTE 的解决方案作为另一个答案。如果您更接近这种情况,或者发布一个问题,详细说明您尝试过的操作。
【解决方案2】:
EXPLAIN 
SELECT parent.fld, child.fld 
  FROM parent JOIN child ON child.id = parent.id  
 UNION ALL 
SELECT parent.fld, child.fld
  FROM parent JOIN child ON child.id = parent.otherid
   AND parent.otherid <> parent.id

使用 MyISAM 引擎的表:

id  select_type   TABLE       TYPE    possible_keys  KEY      key_len  ref                  ROWS  Extra
1   PRIMARY       parent      ALL     PRIMARY                                               9999
1   PRIMARY       child       eq_ref  PRIMARY        PRIMARY  4        test.parent.id       1
2   UNION         parent      ALL     parent_ix1                                            9999  USING WHERE
2   UNION         child       eq_ref  PRIMARY        PRIMARY  4        test.parent.otherid  1
    UNION RESULT  <union1,2>  ALL

使用 InnoDB 引擎的表:

id  select_type   table       type    possible_keys  key         key_len  ref            rows  Extra
1   PRIMARY       child       ALL     PRIMARY                                            9903
1   PRIMARY       parent      eq_ref  PRIMARY        PRIMARY     4        test.child.id  1
2   UNION         child       ALL     PRIMARY                                            9903
2   UNION         parent      ref     parent_ix1     parent_ix1  5        test.child.id  1     Using where
    UNION RESULT  <union1,2>  ALL   

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-24
    • 1970-01-01
    • 2020-03-15
    • 2020-02-17
    • 1970-01-01
    • 2010-11-16
    • 2011-02-15
    • 2018-04-11
    相关资源
    最近更新 更多