MySQL: многие ко многим, как получить все (связанные) категории и объединить запросы - PullRequest
0 голосов
/ 23 января 2012

Таблица:

id | category   (there is a index on id & category)
-----------
1  |  1
1  |  7
1  |  3

2  |  1
2  |  2
2  |  4

3  |  1
3  |  6
3  |  3

SELECT DISTINCT
    category
FROM 
    many_to_many e1     
WHERE id IN 
    (
    SELECT DISTINCT
        e1.id
    FROM 
        many_to_many e1           
    INNER JOIN
        many_to_many x1 
      ON e1.id=x1.id
    WHERE
        e1.category IN (3)
    )

Мне нравится получать возмещение: ** 6, 1, 7 ** (что я получаю с запросом выше)
Мне кажется, что этот запрос не прошел хорошо, потому что подзапрос ищет все идентификаторы, и этот список может быть огромным.

Также не имеет значения, сколько, но если идентификатор связан.
Таким образом, для производительности достаточно было бы 100 проверок идентификатора для каждой уникальной категории, которую нужно заполнить.

Во-вторых, я использую другой запрос (подзапрос), чтобы получить все идентификаторы, которые содержат категорию:

SELECT DISTINCT
        e1.id
    FROM 
        many_to_many e1           
    INNER JOIN
        many_to_many x1 
      ON e1.id=x1.id
    WHERE
        e1.category IN (3)

 returns: 3 & 1

Какой самый эффективный способ запросить результат, который мне нравится? Есть ли более эффективное (лучшее предварительное) решение?
Должен ли я использовать один вместо двух запросов?

Ответы [ 3 ]

2 голосов
/ 23 января 2012
SELECT DISTINCT
        x1.category
    FROM 
        many_to_many e1           
    INNER JOIN
        many_to_many x1 
      ON e1.id = x1.id
    WHERE
        e1.category = 3          --- IN (3) 
      AND
        x1.category <> 3         --- NOT IN (3)

Вы должны проверить план запроса с помощью EXPLAIN.Составной индекс на (category, id) может подойти для этого запроса.

1 голос
/ 23 января 2012

Сначала ваш второй запрос (все идентификаторы, которые содержат категорию 3) не требует объединения с x1 (обратите внимание, как вы вообще его не используете):

SELECT DISTINCT
    e1.id
FROM 
    many_to_many e1           
WHERE
    e1.category IN (3)

Затем ваш первыйзапрос может быть упрощен:

SELECT DISTINCT
    category
FROM 
    many_to_many e1     
WHERE id IN 
    (
    SELECT DISTINCT
        e1.id
    FROM 
        many_to_many e1           
    WHERE
        e1.category IN (3)
    )

Решение

Однако вместо использования подзапроса, который вы можете просто присоединить, что обычно более эффективно, чем подзапрос:

SELECT DISTINCT e2.category 
FROM many_to_many e1 
LEFT JOIN many_to_many e2 ON e1.id=e2.id 
WHERE e1.category IN (3);

Это присоединяет many_to_many к себе на id, что формирует все комбинации категорий для каждого id.В частности, будет строка (3,i) для всех других категорий i, которые отображаются в том же id.

Если вы не хотите, чтобы 3 были включены, измените LEFT JOIN на:

LEFT JOIN many_to_may e2 ON e1.id=e2.id AND e1.category<>e2.category
1 голос
/ 23 января 2012

Возможно, это?

SELECT DISTINCT e2.category
FROM many_to_many e1, many_to_many e2
WHERE
    e1.category='3'
    AND e1.id=e2.id

При этом таблица используется дважды, получая все категории, содержащие идентификатор, имеющий категорию «3».Это также возвращает категорию «3», которую вы можете исключить, если хотите.

...