在MySQL中优化多个JOIN

时间:2018-07-05 17:53:04

标签: mysql join

我正在动态地构建一个SELECT语句,该语句将提取一个项目以及可能包含多个相关项目的列表。

最终目标是在应用程序空间中的一个对象,其中包含每个相关类型的ID数组。

使用JOIN列表非常简单:

SELECT items.*, item_has_related1.related1_id, item_has_related2.related2_id, ...
FROM (items)
LEFT JOIN item_has_related1 ON item_has_related1.item_id = items.id
LEFT JOIN item_has_related2 ON item_has_related2.item_id = items.id
... potentially many more
WHERE items.id = $itemId;
使用

LEFT JOIN是因为某些关系可能为空。

最明显的问题是,返回的行数是所有联接中匹配数的乘积。仅使用几个联接表,该数目可能会很大。如果有五个表,每个表有六个匹配项,那么将有6 ^ 5行!第二个问题是,处理返回行更加复杂,因为我必须在每一列中挖掘出唯一的值。

作为替代方案,我写了类似这样的内容,它基本上对每个JOIN进行单独的查询:

SELECT items.*, item_has_related_1.related1_id, NULL as related2_id, ...
FROM (items)
JOIN item_has_related_1 ON item_has_related_1.item_id = items.id
WHERE items.id = $itemId

UNION

SELECT items.*, NULL as related1_id, item_has_related_2.related2_id, ...
FROM (items)
JOIN item_has_related_2 ON item_has_related_2.item_id = items.id
WHERE items.id = $itemId

以这种方式返回的行数是所有联接中的匹配数之和。但是,查询准备时间要长得多,因此对于较小的数据集,此方法效率较低。我试图凭经验确定“较小”的定义,但是根据测试数据,我不确定结果是否有意义。

是否有更有效的方法来执行多个JOIN并合并结果,还是有另一种方法可以解决此问题?

编辑后添加: Barmar对我的问题有正确的答案,但是我的下一步是扩展where子句以返回多行。提到this question,我的代码最终看起来像这样:

SELECT items.*,
(SELECT GROUP_CONCAT(related1_id) FROM item_has_related_1 WHERE item_id = items.id) as related1Ids,
(SELECT GROUP_CONCAT(related2_id) FROM item_has_related_2 WHERE item_id = items.id) as related2Ids,
...
FROM items
WHERE <where criteria>

2 个答案:

答案 0 :(得分:1)

您可以使用GROUP_CONCAT将每个表中的所有相关项目放入结果中以逗号分隔的列表中。

SELECT items.*, related1_ids, related2_ids, ...
FROM items
LEFT JOIN (
    SELECT item_id, GROUP_CONCAT(related1_id) AS related1_ids
    FROM item_has_related_1
    WHERE item_id = $itemId
) AS r1 ON items.id = r1.item_id
LEFT JOIN (
    SELECT item_id, GROUP_CONCAT(related2_id) AS related2_ids
    FROM item_has_related_2
    WHERE item_id = $itemId
) AS r2 ON items.id = r2.item_id
...

稍后您可以使用应用程序语言对其进行拆分。

答案 1 :(得分:0)

您可以像这样简单地使用内部联接编写查询:

SELECT items.*, item_has_related1.related1_id, item_has_related2.related2_id, ...
FROM (items)
INNER JOIN item_has_related1 ON item_has_related1.item_id = items.id
INNER JOIN item_has_related2 ON item_has_related2.item_id = items.id
... potentially many more
WHERE items.id = $itemId;

此查询的行数与其他表中$itemId的匹配项相同。

问题是,如果您将需要select语句中列出的所有那些数据,则您将必须进行连接所有查询的工​​作,即使它们是分开的,也无法通过使用所有方法获得任何收益如此处列出的那样加入。