当连接在WHERE子句中使用的列时,Mysql在LEFT JOIN中不使用索引

时间:2012-05-10 10:03:46

标签: mysql join where-clause indexing

我已经在mySQL 5.0.51a中解决了这个问题很长一段时间了:

当使用LEFT JOIN连接表并在WHERE子句中使用连接表的列时,mySQL无法在JOIN中使用连接表的主索引,甚至FORCE INDEX(PRIMARY)也失败。

  • 如果连接表的列没有在WHERE子句中,那么一切正常。
  • 如果删除GROUP BY,也会使用索引。

然而我需要他们两个。

故障: (在我的特殊情况下,长达1000秒的执行时间)

SELECT *
FROM tbl_contract co
LEFT JOIN tbl_customer cu ON cu.customer_id = co.customer_id
WHERE cu.marketing_allowed = 1 AND co.marketing_allowed = 1
GROUP BY cu.id
ORDER BY cu.name ASC

工作,但没有解决我的问题:

SELECT *
FROM tbl_contract co
LEFT JOIN tbl_customer cu ON cu.customer_id = co.customer_id
GROUP BY co.id

表结构(转录,因为真实表更复杂)

tbl_contract: 
id: INT(11) PRIMARY
customer_id: INT(11)
marketing_allowed: TINYINT(1)

tbl_customer:
customer_id: INT(11) PRIMARY
marketing_allowed: TINYINT(1)

mySQL EXPLAIN在加入时注意到PRIMARY为可能的键,但不使用它。

有一个解决方案:

SELECT (...)
HAVING cu.marketing_allowed = 1

解决问题 BUT 我们在其他上下文中使用查询,我们只能在整个语句中选择一列,但是HAVING需要在SELECT-Statement中选择marketing_allowed列。 / p>

我还注意到,在所需的表上运行ANALYZE TABLE会使我本地系统上的mySQL 5.5.8做正确的事情,但我不能总是确保在语句之前运行了ANALYZE。无论如何,这个解决方案在我们的高效服务器上的mySQL 5.0.51a下不起作用。 :(

mySQL中是否有一条我没注意到的特殊规则?如果列出现在WHERE子句中,为什么不使用LEFT JOIN索引?为什么我不能强迫他们?

提前,

[编辑]

感谢一些回复,我可以使用INNER JOIN优化查询,但不幸的是,虽然看起来绝对正常,但mySQL在使用ORDER BY子句时仍然拒绝使用索引,我发现:

SELECT *
FROM tbl_contract co
INNER JOIN tbl_customer cu ON cu.customer_id = co.customer_id AND cu.marketing_allowed = 1
WHERE cu.marketing_allowed = 1
ORDER BY cu.name ASC

如果退出ORDER BY,mySQL将正确使用索引。 我删除了GROUP BY,因为它与示例无关。

[EDIT2]

强迫索引也无济于事。所以,问题是:为什么mySQL不使用索引进行连接,因为在加入WHERE子句并减少WHERE子句的结果集后执行ORDER BY?这通常不会影响加入......

3 个答案:

答案 0 :(得分:1)

我不确定我明白你在问什么,但是

SELECT *
FROM tbl_contract co
LEFT JOIN tbl_customer cu ON cu.customer_id = co.customer_id
WHERE cu.marketing_allowed = 1 AND co.marketing_allowed = 1

不会进行外连接(因为cu.marketing_allowed = 1)。

您可能打算使用:

SELECT *
FROM tbl_contract co
   LEFT JOIN tbl_customer cu 
        ON cu.customer_id = co.customer_id
       AND cu.marketing_allowed = 1 
WHERE co.marketing_allowed = 1

答案 1 :(得分:1)

我遇到了同样的麻烦。在条件使用JOIN时,MySQL优化器不使用索引。我将我的SQL语句从JOIN更改为子查询:

SELECT
    t1.field1,
    t1.field2,
    ...
    (SELECT
        t2.field3
        FROM table2 t2
        WHERE t2.fieldX=t1.fieldX
    ) AS field3,
    (SELECT
        t2.field4
        FROM table2 t2
        WHERE t2.fieldX=t1.fieldX
    ) AS field4,
FROM table1 t1
WHERE t1.fieldZ='valueZ'
ORDER BY t1.sortedField

这个请求要复杂得多,但是当使用索引时,它的速度也会快得多。

您也可以使用STRAIGHT_JOIN但上述查询的效果会更好。这是DB与table1中的100k行和table2中的20k的比较:

    使用以上查询
  • 0.00s
  • 0.10s使用STRAIGHT_JOIN
  • 0.30使用JOIN

答案 2 :(得分:0)

您是否在JOIN子句中尝试了多个条件?

SELECT *
FROM tbl_contract co
LEFT JOIN tbl_customer cu ON cu.customer_id = co.customer_id AND cu.marketing_allowed = 1
WHERE co.marketing_allowed = 1