使用LEFT JOIN优化JOIN

时间:2011-05-26 04:16:07

标签: mysql sql join query-optimization left-join

我在优化此查询时遇到问题:

SELECT a.id
FROM a
JOIN b ON a.id=b.id
LEFT JOIN c ON a.id=c.id
WHERE
   (b.c1='12345' OR c.c1='12345')
   AND (a.c2=0 OR b.c3=1)
   AND a.c4='active'
GROUP BY a.id;

查询需要7秒,而bc中只有一个是JOIN时需要0。解答:

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
         type: ref
possible_keys: PRIMARY(id),c4,c2
          key: c4
      key_len: 1
          ref: const
         rows: 80775
        Extra: Using where; Using temporary; Using filesort
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: c
         type: ref
possible_keys: id_c1_unique,id
          key: id_c1
      key_len: 4
          ref: database.a.id
         rows: 1
        Extra: Using index
*************************** 3. row ***************************
           id: 1
  select_type: SIMPLE
        table: b
         type: ref
possible_keys: id_c1_unique,id,c1,c3
          key: id
      key_len: 4
          ref: database.a.id
         rows: 2
        Extra: Using where

b始终只有一个匹配的行,c最多只有一个匹配的行。如果MySQL首先获得与b文字匹配的cc1行,然后根据a加入id,那么速度会快得多以a开头。

详细说明:

  • 的MyISAM
  • 所有列都有索引(_unique是UNIQUE)
  • 所有列均为非空

我尝试了什么:

  • 更改JOIN的顺序
  • 将WHERE条件移至ON子句
  • b.c1c.c1的子选择(WHERE b.id =(SELECT b.id FROM b WHERE c1 ='12345'))
  • bc
  • 的USE INDEX

我知道我可以使用带有UNION的两个SELECT来执行此操作,但是如果可能的话,我需要避免这种情况,因为查询是如何生成的。

编辑:添加CREATE TABLE

带有相关列的

CREATE TABLE

CREATE TABLE `a` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `c2` tinyint(1) NOT NULL,
  `c4` enum('active','pending','closed') NOT NULL,
  PRIMARY KEY (`id`),
  KEY `c2` (`c2`)
  KEY `c4` (`c4`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE `b` (
    `b_id` int(11) NOT NULL AUTO_INCREMENT,
    `id` int(11) NOT NULL DEFAULT '0',
    `c1` int(11) NOT NULL,
    `c3` tinyint(1) NOT NULL,
    PRIMARY KEY (`b_id`),
    UNIQUE KEY `id_c1_unique` (`id`,`c1`),
    KEY `c1` (`c1`),
    KEY `c3` (`c3`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE `c` (
    `c_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `id` int(11) NOT NULL,
    `c1` int(11) NOT NULL,
    PRIMARY KEY (`c_id`),
    UNIQUE KEY `id_c1_unique` (`id`,`c1`),
    KEY `id` (`id`),
    KEY `c1` (`c1`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

3 个答案:

答案 0 :(得分:0)

不是正面的,但我很确定改变连接的顺序,并且在条件与on子句无关的地方移动。

我不确定这里有足够的信息可以确定,但我猜“所有列都有索引”是你的问题。对于任何特定查询,每个表只使用一个索引。所以,如果你在a.id上有一个索引,在a.c2上有一个单独的索引,在a.c4上有一个索引。好吧,它只会使用一个。

似乎索引中有几列。所以,你只加入2个表,可以免费使用“有用的”索引。

我的建议是检查索引并让它们覆盖此查询正在使用的正确字段(如果可能)。

索引ID& c2& C4 b id和amp;的索引c1& C3 关于id&的c索引C1

答案 1 :(得分:0)

select STRAIGHT_JOIN 
      distinct a.ID
   from
      a
         join b
            on a.ID = b.ID
         left join c
            on a.id = c.id
            and c.c1 = '12345'
   where
          a.C4 = 'active'
      and ( a.c2 = 0 or b.c3 = 1 )
      and ( b.c1 = '12345' or c.c1='12345' )

答案 2 :(得分:0)

OP在这里回答。

我已经确定的是,我看到MySQL首先读取效率较低的表的行为是所有LEFT JOIN的固有问题,其中效率较低的表位于左侧。根据MySQL手册中的LEFT JOIN and RIGHT JOIN Optimization

  

MySQL实现A LEFT JOIN B join_condition,如下所示:

     
      
  • 表格B设置为依赖于表格A以及A所依赖的所有表格
  •   

所以:

SELECT a.id
FROM a
LEFT JOIN c ON a.id=c.id
GROUP BY a.id;
即使查询计划显示阅读a更有效,

也会始终首先阅读c。切换表会导致MySQL首先从c读取:

SELECT a.id
FROM c
LEFT JOIN a ON c.id=a.id
GROUP BY a.id;

在我的情况下,两个查询都返回相同的结果。显然,我缺少了一些概念性的东西,它要求在进行LEFT JOIN时始终首先读取左侧表。在我看来,右侧表可以很容易地首先读取,MySQL仍然可以生成相同的结果(对于某些查询,不一定是所有LEFT JOIN)。如果可能的话虽然可能很久以前就已经添加了优化,所以我想我只是错过了这个概念。

最后切换表的顺序对我来说不是一个好的解决方案。我最终将bc合并到一个表中,这简化了应用程序,应该从一开始就完成。使用单个表我可以进行JOIN而不是LEFT JOIN,完全避免了这个问题。

另一种可能的解决方案可能是创建一个包含两个表的视图,从而为JOIN提供单个视图。我没有测试过。

TL; DR:更改表的顺序以使效率最高(如果,则结果集相同,无论顺序如何)。或者将bc合并到一个表中。或者可能会创建一个结合bc

的视图