2012-05-10 103 views
2

我一直百思不得其解解决此问题在MySQL的5.0.51a现在相当长的一段:在mysql LEFT不使用索引联接WHERE子句

当使用LEFT JOIN加入一个表,并使用WHERE子句中连接表的列,mySQL无法使用JOIN中连接表的主索引,即使FORCE INDEX(PRIMARY)也失败。

  • 如果未连接的表的列在WHERE子句中,则一切正常。
  • 如果删除GROUP BY,则也使用该索引。

但我需要他们两个。

故障: (的EXEC时间在我的特殊情况下可达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 

解决了我们使用的查询在其他情况下,我们只能选择在整个声明中一列问题,但有需求的marketing_allowed列在SELECT语句中被选中。

我也注意到,在所需的表上运行ANALYZE TABLE将会使mySQL 5.5.8在我的本地系统上做正确的事情,但我不能始终确保ANALYZE已经在语句前运行。无论如何,这个解决方案在我们的生产服务器上不能在mySQL 5.0.51a下运行。 :(

有MySQL中的特殊规则,我没注意到?为什么LEFT JOIN不使用索引,如果列显示在WHERE子句中?为什么我不能强迫他们?提前

THX ,

[编辑]

由于一些答复,我可以用一个订单时使用INNER JOIN优化查询,但遗憾的是,虽然看似精绝,MySQL的仍然拒绝使用索引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,因为它在示例中没有相关性。

[编辑2]

FORCING索引也没有帮助。所以,问题是:为什么mySQL不使用索引进行连接,因为在连接和减少WHERE子句的结果集之后执行ORDER BY?这通常不应该影响加入...

+1

你怎么知道“mySQL无法使用主索引”?你检查过'EXPLAIN'的输出吗?你能把它包括在你的问题中吗? – eggyal

+0

请分享表格结构。 –

+0

嗨,是的,我大量使用了EXPLAIN,午餐休息后我会修改我的请求;) –

回答

0

我不知道我理解你的要求,但

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 
+0

正如另一篇文章中提到的那样,我在整理没有客户关联的合同时遇到了一个问题。如果cu.customer_id不为NULL,将导致mySQL不使用索引 –

+1

@RenéHerzog:显然你不想要一个外连接,所以你为什么要使用它? –

+0

好吧,我只知道这个,我已经在研究,什么样的加入会是最好的? –

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 
+0

嗨,是的,这是我的第一个想法。必须整理没有加入客户的线路,因为他禁止获得营销信息。 - 这导致了WHERE ... AND cu。customer_id不是NULL,这也会产生INDEX未被使用的问题。 :( –

+0

我怕我没有得到“理清那些没有加入客户的台词”如果你的意思是淘汰,我相信你会一直使用INNER JOIN代替LEFT JOIN的..请解释一下更 –

+0

也许我可能是一个不同的JOIN类型可以解决我的问题,我会检查并报告thx至此 –

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,但上述查询的性能会更好。这是一个有表1中的10万行和20K表2中的比较通过DB:使用上面的查询

  • 0.10s

    • 0.00S使用STRAIGHT_JOIN
    • 0.30使用JOIN