【发布时间】:2012-05-18 21:23:25
【问题描述】:
我已经为 mySQL 5.0.51a 中的这个问题困惑了很长时间:
当使用 LEFT JOIN 连接表 AND 在 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
解决了问题但是我们在其他上下文中使用查询,我们只能在整个语句中选择一列,但 HAVING 需要在 SELECT-Statement 中选择marketing_allowed 列。
我还注意到,在所需的表上运行 ANALYZE TABLE 将使我的本地系统上的 mySQL 5.5.8 做正确的事情,但我不能总是保证 ANALYZE 已经在语句之前运行。无论如何,这个解决方案在我们的生产服务器上的 mySQL 5.0.51a 下不起作用。 :(
mySQL 中是否有我没有注意到的特殊规则?如果列出现在 WHERE 子句中,为什么不使用 LEFT JOIN 索引?为什么我不能强迫他们?
提前谢谢,
勒内
[编辑]
感谢一些回复,我可以使用 INNER JOIN 优化查询,但不幸的是,虽然看起来绝对没问题,但当我发现使用 ORDER BY 子句时,mySQL 仍然拒绝使用索引:
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]
强制索引也无济于事。所以,问题是:为什么 mySQL 不使用索引来加入,因为 ORDER BY 是在加入之后执行的,并通过 WHERE 子句减少结果集?这通常不会影响加入...
【问题讨论】:
-
怎么知道“mySQL使用主索引失败”?你检查过
EXPLAIN的输出吗?你能把它包括在你的问题中吗? -
请分享表结构。
-
嗨,是的,我大量使用EXPLAIN,午休后我会修改我的请求;)
-
刚刚编辑它...显然,我使用的 GROUP BY 会影响行为,之前没有注意到...谢谢您的帮助。
-
我刚刚发现我最后的 ORDER BY cu.customer_name ASC 也是阻止它的一个因素。但是 ORDER BY 总是在 WHERE 之后执行,因此不应该影响在连接上使用索引,不是吗?
标签: mysql join where-clause indexing