Оптимизация нескольких JOIN в MySQL - PullRequest
0 голосов
/ 05 июля 2018

Я динамически создаю инструкцию SELECT, которая выбирает элемент и, возможно, несколько списков связанных элементов.

Конечной целью является объект в пространстве приложения с массивами идентификаторов для каждого из связанных типов.

Использование списка 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 для возврата нескольких строк. Ссылаясь на этот вопрос , мой код в итоге выглядел так:

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 голосов
/ 05 июля 2018

Вы можете использовать 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
...

Позже вы можете разделить их на языке приложения.

0 голосов
/ 05 июля 2018

Вы можете просто написать запрос с внутренними объединениями так:

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, вам придется выполнить работу по объединению всех запросов, даже если они являются отдельными, что ничего не даст по сравнению с подходом выполнения всех объединяет, как указано здесь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...