Вопрос

У меня есть связь HABTM между "статьями" и "тегами"

Проблема: Я ищу только статьи с тегами "спорт" и "на открытом воздухе", но не статьи только с одним из этих тегов.

Я попробовал это:

SELECT DISTINCT article.id, article.name FROM articles
inner JOIN tags ON (tags.name IN ('outdoors', 'sports')
inner JOIN articles_tags ON articles_tags.article_id = article.id AND articles_tags.tag_id = tags.id

...но это дает мне статьи, посвященные только спорту, только на открытом воздухе И обоим видам спорта + на открытом воздухе

Вопрос какой правильный запрос использовать?(Я использую MySQL)

Это было полезно?

Решение

Есть два распространенных решения.

  • Первое решение использует GROUP BY подсчитывает теги для каждой статьи, которые соответствуют "outdoors" или "sports", а затем возвращает только группы, у которых есть оба тега.

    SELECT a.id, a.name
    FROM articles AS a
    INNER JOIN articles_tags AS at ON (a.id = at.article_id)
    INNER JOIN tags AS t ON (t.id = at.tag_id)
    WHERE t.name IN ('outdoors', 'sports')
    GROUP BY a.id
    HAVING COUNT(DISTINCT t.name) = 2;
    

    Некоторым людям это решение кажется более понятным, а добавление значений более простым.Но GROUP BY запросы в MySQL, как правило, приводят к созданию временной таблицы, что снижает производительность.

  • Другое решение использует JOIN для каждого отдельного тега.При использовании внутренних объединений запрос, естественно, ограничивается статьями, которые соответствуют всем указанным вами тегам.

    SELECT a.id, a.name
    FROM articles AS a
    INNER JOIN articles_tags AS at1 ON (a.id = at1.article_id)
    INNER JOIN tags AS t1 ON (t1.id = at1.tag_id AND t1.name = 'outdoors')
    INNER JOIN articles_tags AS at2 ON (a.id = at2.article_id)
    INNER JOIN tags AS t2 ON (t2.id = at2.article_id AND t2.name = 'sports');
    

    Предполагая tags.name и articles_tags.(article_id,tag_id) у обоих есть UNIQUE ограничений, вам не должен понадобиться DISTINCT модификатор запроса.

    Этот тип запросов, как правило, лучше оптимизируется в MySQL, чем GROUP BY решение, предполагая, что вы определили соответствующие индексы.


Повторите ваш последующий вопрос в комментарии, я бы сделал что-то вроде этого:

SELECT a.id, a.name, GROUP_CONCAT(t3.tag) AS all_tags
FROM articles AS a
INNER JOIN articles_tags AS at1 ON (a.id = at1.article_id)
INNER JOIN tags AS t1 ON (t1.id = at1.tag_id AND t1.name = 'outdoors')
INNER JOIN articles_tags AS at2 ON (a.id = at2.article_id)
INNER JOIN tags AS t2 ON (t2.id = at2.article_id AND t2.name = 'sports');
INNER JOIN articles_tags AS at3 ON (a.id = at3.article_id)
INNER JOIN tags AS t3 ON (t3.id = at3.article_id);
GROUP BY a.id;

Это по-прежнему находит только статьи с обоими тегами "outdoors" и "sports", но затем дополнительно объединяет эти статьи с ВСЕ его теги.

Это вернет несколько строк для каждой статьи (по одной для каждого тега), поэтому затем мы используем GROUP BY чтобы снова сократить количество строк для каждой статьи до одной. GROUP_CONCAT() возвращает разделенный запятыми список значений в соответствующей группе.

Другие советы

Попробуй это:

SELECT a1.id, a1.name FROM articles a1
    JOIN tags t1 ON t1.name ='outdoors'
    JOIN articles_tags at1 ON at1.article_id = a1.id AND at1.tag_id = t1.id
    JOIN tags t2 ON t2.name = 'sports'
    JOIN articles_tags at2 ON at2.article_id = a1.id AND at2.tag_id = t2.id
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top