Улучшение MySQL Query с IN Subquery - PullRequest
4 голосов
/ 15 августа 2011

У меня есть таблица items и таблица item_attributes.

Для простоты, скажем, у моего элемента таблицы есть столбец id и столбец name.Конечно, есть индекс по столбцу id.

таблица item_attributes содержит столбцы id, item_id, attribute_name и attribute_value и индекс ON attrubute_name

Теперь я хочу запросить все элементы с определенным атрибутом без использования объединения.

Я делаю это с помощью следующего запроса:

SELECT *
FROM items i
WHERE i.id IN (
    SELECT item_id
    FROM item_attributes a
    WHERE a.attribute_name = 'SomeAttribute'
      AND a.attribute_value = 'SomeValue'
)

сам подзапрос выполняется быстро.

Если я сначала выполню сам запрос и использую результат для запроса IN

SELECT *
FROM items i
WHERE i.id IN (1,3,5,7,10,...)

, это тоже быстро.

Однако объединенный запрос оченьочень медленно (> 2 сек.) Если я изучу план запроса, я пойму почему: MySQL выполняет полное сканирование таблицы таблицы элементов вместо того, чтобы сначала выполнить подзапрос и использовать результат для запроса индекса.

1, 'PRIMARY', 'items', 'ALL', '', '', '', '', 149726, 'Using where'
2, 'DEPENDENT SUBQUERY', 'item_attributes', 'index_subquery', 'IDX_ATTRIBUTE_NAME', 'IDX_ATTRIBUTE_NAME', '4', 'func', 1, 'Using where'

Есть ли способ оптимизировать этот запрос?Я знаю, что подзапрос всегда будет возвращать только небольшой набор результатов (<100 строк). </p>

Ответы [ 4 ]

5 голосов
/ 15 августа 2011

MySQL не может переключить ведущую и ведомую таблицу в предложении IN. Это будет исправлено в 6.0.

На данный момент вы можете переписать его следующим образом (требуется JOIN):

SELECT  i.*
FROM    (
        SELECT  DISTINCT item_id
        FROM    item_attributes a
        WHERE   a.attribute_name = 'SomeAttribute'
                AND a.attribute_value = 'SomeValue'
        ) ai
JOIN    items i
ON      i.id = ai.item_id

Поскольку вы используете модель EAV, вы можете создать уникальный индекс для (attribute_name, item_id), и в этом случае вы можете использовать простое соединение:

SELECT  i.*
FROM    item_attributes ai
JOIN    items i
ON      i.id = ai.item_id
WHERE   a.attribute_value = 'SomeValue'
        AND a.attribute_name = 'SomeAttribute'
1 голос
/ 15 августа 2011

внутреннее объединение работает лучше и эффективнее

select i.* 
from items i
inner join item_attributes ia on i.id=ia.item_id
where ia.attribute_name='SomeAttribute' AND ia.attribute_value='SomeValue';

, если первичный ключ для item_attributes - для item_id + attribute_name,
, тогда GROUP BY не требуется

0 голосов
/ 15 августа 2011
SELECT DISTINCT i.*
FROM items i, item_attributes ai
WHERE i.id = ai.item_id AND a.attribute_name = 'SomeAttribute' AND a.attribute_value = 'SomeValue'
0 голосов
/ 15 августа 2011

Вы можете использовать exists

SELECT *
FROM items i
WHERE Exists
(
    SELECT item_id
    FROM item_attributes a
    WHERE 1=1
      AND i.id = a.ItemId
      AND a.attribute_name = 'SomeAttribute'
      AND a.attribute_value = 'SomeValue'
)
...