Mysql многие ко многим запрос отношения.Как получить все теги отфильтрованных сообщений? - PullRequest
3 голосов
/ 03 февраля 2012

Я рассмотрел множество вопросов по этой проблеме здесь, в Stackoverflow, но я думаю, что это не так.

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

У меня есть таблицы записей , posts_tags и теги . У posts_tags есть post_id и tag_id. Мне удалось получить post_ids с определенным набором тегов:

SELECT pt.post_id
FROM posts_tags pt
    INNER JOIN tags t
        ON pt.tag_id = t.id
WHERE t.name IN ('tag1', 'tag2', 'tag3')
GROUP BY pt.post_id
HAVING COUNT(DISTINCT t.id) = 3;

Допустим, этот запрос дает post_ids 1, 2, 3:

post 1 has tag1, tag2, tag3 and tag4
post 2 has tag1, tag2, tag3 and tag5
post 3 has tag1, tag2, tag3 and tag6

Теперь моя проблема заключается в , как расширить запрос так, чтобы он возвращал пользователю только tag4, tag5 и tag6 , поскольку эти теги по-прежнему доступны для дальнейшей фильтрации сообщений. Как этого добиться?

Было бы неплохо обратить внимание на производительность. У меня 130000 постов, 6500 тегов, а в бридж-таблице 240000 строк.

редактировать: использовать сценарий:

  1. Пользователь отправляет теги с автозаполнением и выбирает несколько тегов.
  2. Пользователь получает сообщения на основе отправленных тегов.
  3. Пользователь ищет больше тегов и в этот момент:

    Я не хочу давать полный список, а только те, которые

    а. Это еще не было выбрано.

    б. Используются в сообщениях, которые были получены на шаге 2.

Sample data


РЕДАКТИРОВАТЬ: ЗАКЛЮЧИТЕЛЬНЫЙ ЗАПРОС НА ОСНОВЕ ОТВЕТА Мости Мостачо:

SELECT DISTINCT pt2.tag_id, t2.name FROM    
(SELECT pt1.post_id
    FROM posts_tags pt1
    INNER JOIN tags t1
        ON pt1.tag_id = t1.id
    WHERE t1.name in ('tag1','tag2','tag3')
    GROUP BY pt1.post_id
    HAVING COUNT(DISTINCT t1.id) = 3) MatchingPosts
INNER JOIN posts_tags pt2 ON (MatchingPosts.post_id = pt2.post_id)
INNER JOIN tags t2 ON (pt2.tag_id = t2.id)
WHERE t2.name NOT IN ('tag1','tag2','tag3');

Ответы [ 2 ]

2 голосов
/ 04 февраля 2012

Ну, это лучшее, что я могу вспомнить в 4:30 утра:

SELECT distinct tag_id FROM
    (SELECT pt1.post_id FROM pt1
    INNER JOIN tags t1 ON (pt1.tag_id = t1.id)
    WHERE t1.id IN (1, 2)
    GROUP BY pt1.post_id
    HAVING COUNT(DISTINCT t1.id) = 2) MatchingPosts
INNER JOIN pt2 ON (MatchingPosts.post_id = pt2.post_id)
WHERE (pt2.tag_id NOT IN (1, 2))

(1, 2) - это теги, которые вы ищете, и количество, конечно, должно соответствовать количеству тегов, которые вы используете для фильтрации.

Вот пример (обратите внимание, я немного изменил данные)

0 голосов
/ 03 февраля 2012

Что вы подразумеваете под расширением, чтобы включить только tag4, tag5, tag6 ... почему бы просто не изменить ваше WHERE t.name в (), чтобы отразить эти теги ...

ИЛИ ... Вы имеете в виду, что ДОЛЖНЫ включать теги 1, 2, 3, но также и ОДИН из (tag4 или tag5 или tag6) ...

Если бы это было так, я бы изменил где / имея следующее ...

WHERE t.name IN ('tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' )
GROUP BY pt.post_id
HAVING sum( if( t.name in ('tag1', 'tag2', 'tag3' ), 1, 0 )) = 3
  AND sum( if( t.name in  ('tag4', 'tag5', 'tag6' ), 1, 0 )) > 0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...