3

我已经在 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 子句减少结果集?这通常不应该影响加入...

4

3 回答 3

1

I'm not sure I understand what you are asking, but

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

will not do an outer join (because of cu.marketing_allowed = 1).

You probably meant to use:

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
于 2012-05-10T11:32:20.207 回答
1

我有同样的麻烦。MySQL 优化器在使用带条件的 JOIN 时不使用索引。我将我的 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
于 2017-01-24T13:09:19.050 回答
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
于 2012-05-10T11:36:06.757 回答