Нужна помощь с SQL-запросом, чтобы найти вещи, отмеченные всеми указанными тегами - PullRequest
5 голосов
/ 06 октября 2010

Допустим, у меня есть следующие таблицы:

TAGS

id: целое число
name: string

POSTS

id: integer
body: text

TAGGINGS

id: целое число
tag_id: целое число
post_id: целое число

Как мне написать запрос, который выберет всесообщения, которые отмечены ВСЕМИ из следующих тегов (атрибут name из таблицы тегов): "Сыр", "Wine", "Paris", "Frace", "City", "Scenic", "Art"

Смотрите также: Нужна помощь с SQL-запросом, чтобы найти вещи с большинством указанных тегов (примечание: похоже, но не дубликат!)

Ответы [ 2 ]

17 голосов
/ 06 октября 2010

Использование IN:

SELECT p.*
  FROM POSTS p
 WHERE p.id IN (SELECT tg.post_id
                  FROM TAGGINGS tg
                  JOIN TAGS t ON t.id = tg.tag_id
                 WHERE t.name IN ('Cheese','Wine','Paris','Frace','City','Scenic','Art')
              GROUP BY tg.post_id
                HAVING COUNT(DISTINCT t.name) = 7)

Использование JOIN

SELECT p.*
  FROM POSTS p
  JOIN (SELECT tg.post_id
          FROM TAGGINGS tg
          JOIN TAGS t ON t.id = tg.tag_id
         WHERE t.name IN ('Cheese','Wine','Paris','Frace','City','Scenic','Art')
      GROUP BY tg.post_id
        HAVING COUNT(DISTINCT t.name) = 7) x ON x.post_id = p.id

Использование EXISTS

SELECT p.*
  FROM POSTS p
 WHERE EXISTS (SELECT NULL
                 FROM TAGGINGS tg
                 JOIN TAGS t ON t.id = tg.tag_id
                WHERE t.name IN ('Cheese','Wine','Paris','Frace','City','Scenic','Art')
                  AND tg.post_id = p.id
             GROUP BY tg.post_id
               HAVING COUNT(DISTINCT t.name) = 7)

Объяснение

Суть в том, что COUNT(DISTINCT t.name) должно соответствовать количеству имен тегов, чтобы гарантировать, что все эти теги связаны с постом. Без DISTINCT существует риск, что дубликаты одного из имен могут вернуть счетчик 7, так что вы получите ложное срабатывание.

Производительность

Большинство скажет вам, что соединение является оптимальным, но соединения также рискуют дублировать строки в наборе результатов. EXISTS будет моим следующим выбором - нет дублирующегося риска и, как правило, более быстрое выполнение, но проверка плана объяснения в конечном итоге скажет вам, что лучше всего, исходя из ваших настроек и данных.

2 голосов
/ 06 октября 2010

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

Select * From Posts p
   Where Not Exists
       (Select * From tags t
        Where name in 
           ('Cheese', 'Wine', 'Paris', 
             'Frace', 'City', 'Scenic', 'Art')
           And Not Exists
             (Select * From taggings
              Where tag_id = t.Tag_Id
                And post_Id = p.Post_Id))

Объяснение : Запрос списка тех сообщений, которые имели каждое указанного набора тегов, связанных с ним эквивалентно для запроса тех сообщений, где есть тег нет в том же указанном наборе, который имеет не был связан с этим.т.е. выше sql.

...