SQL-запрос: моделирование «И» над несколькими строками вместо подзапроса - PullRequest
10 голосов
/ 02 октября 2008

Предположим, у меня есть таблица "тегов" с двумя столбцами: tagid и contentid . Каждая строка представляет тег, назначенный для части содержимого. Я хочу запрос, который даст мне содержание каждой части контента, которая помечена тегами 334, 338 и 342.

Самый простой способ сделать это ( псевдокод ):

select contentid from tags where tagid = 334 and contentid in (
    select contentid from tags where tagid = 338 and contentid in (
        select contentid from tags where tagid = 342
    )
)

Однако моя интуиция говорит мне, что есть лучший, более быстрый и более расширяемый способ сделать это. Например, что если мне нужно найти пересечение 12 тегов? Это может быстро стать ужасным. Есть идеи?

РЕДАКТИРОВАТЬ : Оказывается, это также описано в этом отличном сообщении в блоге .

Ответы [ 5 ]

24 голосов
/ 02 октября 2008
SELECT contentID
FROM tags
WHERE tagID in (334, 338, 342)
GROUP BY contentID
HAVING COUNT(DISTINCT tagID) = 3


--In general
SELECT contentID
FROM tags
WHERE tagID in (...) --taglist
GROUP BY contentID
HAVING COUNT(DISTINCT tagID) = ... --tagcount
2 голосов
/ 20 ноября 2008

Вот решение, которое сработало намного быстрее, чем для меня, для очень большой базы данных объектов и тегов. Это пример пересечения из трех тегов. Он просто объединяет множество соединений в таблице тегов объекта (objtags) для обозначения одного и того же объекта и определяет идентификаторы тегов в предложении WHERE:

SELECT w0.objid

FROM       objtags t0
INNER JOIN objtags t1 ON t1.objid=t0.objid
INNER JOIN objtags t2 ON t2.objid=t1.objid

WHERE t0.tagid=512
  AND t1.tagid=256
  AND t2.tagid=128

Понятия не имею, почему это работает быстрее. Он был вдохновлен поисковым кодом на сервере MusicBrainz. Делая это в Postgres, я обычно получаю ускорение в ~ 8-10 раз по сравнению с решением HAVING COUNT(...).

1 голос
/ 02 октября 2008

Единственный альтернативный способ, которым я могу придумать, это:

select a.contentid from tags a
inner join tags b on a.contentid = b.contentid and b.tagid=334
inner join tags c on a.contentid = c.contentid and c.tagid=342
where a.tagid=338
0 голосов
/ 02 октября 2008

Я не знаю, лучше ли это, но это может быть более ремонтопригодным

select contentid from tags where tagid = 334
intersect
select contentid from tags where tagid = 338
intersect
select contentid from tags where tagid = 342

Вы должны построить его динамически, что будет не так плохо, как ваше оригинальное решение.

0 голосов
/ 02 октября 2008

Какой тип SQL? MS SQL Server, Oracle, MySQL?

В SQL Server это не равно:

select contentid from tags where tagid IN (334,338,342)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...