如果在 ON 子句中使用 OR,MySQL 将不会在 JOIN 中使用可用索引

Posted

技术标签:

【中文标题】如果在 ON 子句中使用 OR,MySQL 将不会在 JOIN 中使用可用索引【英文标题】:MySQL won't use available indexes in JOIN if OR is used in ON clause 【发布时间】:2012-03-06 01:02:03 【问题描述】:

假设您有 5 个表格,每个表格都有它们的列:

房子(id、name、street_id) 街道(ID、名称) 照片(身份证、姓名) house_photo (house_id, photo_id) street_photo (street_id, photo_id)

并说所有 'id' 列和以 '_id' 结尾的列已经有索引。

(实际上我的查询与房屋或街道无关。它们是为了争论。)

现在假设您想将每条街道分成一列,如果那条街道或其房屋有照片,您希望下一列中的照片...

这里的棘手之处在于所有房子都在一张桌子上。以及另一张桌子上的所有照片。但是要链接 2,我们需要访问所有五个表。

我想出了以下查询,包含 4 个 JOIN:

SELECT
    street.name
    ,group_concat(distinct photos.name SEPARATOR '\n') as photos
FROM
    house
    INNER JOIN street ON
        house.street_id = street.id
    LEFT JOIN house_photos ON
        house.id = house_photos.house_id
    LEFT JOIN street_photos ON
        street.id = street_photos.street_id
    LEFT JOIN photos ON
        photos.id = house_photos.photo_id
        OR photos.id = street_photos.photo_id
GROUP BY
    street.name

distinct 用于过滤掉双打,因为当您有超过 1 张房子的照片和超过 1 张房子街道的照片时会生成它们。 (Carthesian 积)但这与我的问题无关。

我遇到的问题是查询超级慢。 (需要超过 1 分钟甚至更长的时间才能完成)

当我要求 mysql 分析查询('explain extended')时,我发现它在处理最后一个 JOIN(在 ON 子句中有 OR)时不会使用可用索引。

如果我将最后一个 JOIN 拆分为 2 个 JOIN,(从而添加第五个 JOIN),查询再次变得非常快。(需要一秒钟才能完成。)

SELECT
    street.name
    ,concat(
        group_concat(distinct photos_from_house.name SEPARATOR '\n')
        ,'\n'
        ,group_concat(distinct photos_from_street.name SEPARATOR '\n')
    ) as photos
FROM
    house
    INNER JOIN street ON
        house.street_id = street.id
    LEFT JOIN house_photos ON
        house.id = house_photos.house_id
    LEFT JOIN street_photos ON
        street.id = street_photos.street_id
    LEFT JOIN photos photos_from_house ON
        photos_from_house.id = house_photos.photo_id
    LEFT JOIN photos photos_from_street ON
        photos_from_street.id = street_photos.photo_id
GROUP BY
    street.name

我现在的问题是:为什么在 ON 子句中引入 OR,使 MySQL 不使用该 JOIN 的可用索引/键?

我已经尝试过使用 USE INDEX 和 FORCE INDEX,但它不会让步。

欢迎任何解释/帮助。

【问题讨论】:

【参考方案1】:

The MySQL docs有话要说:

尽量减少 WHERE 子句中的 OR 关键字。如果没有索引 这有助于定位 OR 两侧的值,任何行都可以 可能是结果集的一部分,因此必须测试所有行,并且 这需要全表扫描。如果您有一个索引可以帮助您 优化 OR 查询的一侧,以及有助于 优化另一边,使用 UNION 算子分开跑得快 之后查询并合并结果。

不幸的是,这并不能真正回答您的问题,正如您所说,您在所有相关列上都有索引。

【讨论】:

事实上我认为确实如此......我会等着看是否有更合适的答案出现。但正如你所引用的,我有一个索引用于 OR 的一侧和 1 的另一侧。因此,根据您的报价,我应该使用 UNION 并合并...尽管它确实变得混乱,而且我当前的解决方法(额外的 JOIN)似乎更整洁。

以上是关于如果在 ON 子句中使用 OR,MySQL 将不会在 JOIN 中使用可用索引的主要内容,如果未能解决你的问题,请参考以下文章

使用关键字与 ON 子句 - MYSQL [重复]

MySQL“On 子句中的未知列”[重复]

Sqoop 导入失败,“on 子句”中有未知列

mysql子查询在where in子句中

MySQL错误1054未知列'persons.PersonID' in on子句[重复]

MySQL 多表连接内连接