我已经在 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]
FORCING 索引也无济于事。所以,问题是:为什么 mySQL 不使用索引来加入,因为 ORDER BY 是在加入之后执行的,并通过 WHERE 子句减少结果集?这通常不应该影响加入...