Выбор первичного из строки сопоставления SQL - PullRequest
2 голосов
/ 12 марта 2012

У меня есть таблица, которая ссылается на кучу статей, таблица содержит теги для этих статей.Например:

tag text
article_id bigint

Я хочу выбрать все article_ids с набором тегов, скажем, tag1, tag2, tag3, но к статье также могут быть прикреплены теги tag4, tag5.

Я знаю, что это будет работать:

SELECT article_id 
FROM tag WHERE tag='tag1' 
INTERSECT 
SELECT article_id 
FROM tag 
WHERE tag='tag2' 
INTERSECT 
SELECT article_id 
FROM tag 
WHERE tag='tag3'

И так будет:

SELECT article_id 
FROM tag 
WHERE tag IN ('tag1','tag2','tag3') 
GROUP BY article_id 
HAVING count(*) = 3

Но я не уверен, что это самый эффективный способ сделать это.Я также играл с приведенным ниже, но не могу заставить его работать в настоящее время.

SELECT array_agg(tag) as arr,
       article_id 
FROM tag 
GROUP BY article_id 
HAVING arr = {tag1,tag2,tag3}

Это просто казалось распространенной проблемой, с которой столкнутся другие, мне было интересно, является ли INTERSECT наиболее эффективным запросом вэтот случай.Это для PostgreSQL.

Ответы [ 3 ]

1 голос
/ 12 марта 2012

Полагаю, вам нужно больше столбцов из article, чем просто article_id.Тем не менее, стиль запроса не очень сильно меняется.

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

Мои личные фавориты (и весьма вероятно средисамые быстрые):

SELECT a.*
FROM   article a
JOIN   tag x USING (article_id)
JOIN   tag y USING (article_id)
JOIN   tag z USING (article_id)
WHERE  x.tag = 'tag1'
AND    y.tag = 'tag1'
AND    z.tag = 'tag3';

Или:

SELECT a.*
FROM   article a
WHERE  EXISTS (
   SELECT *
   FROM   tag x
   JOIN   tag y USING (article_id)
   JOIN   tag z USING (article_id)
   WHERE  x.article_id = a.article_id
   AND    x.tag = 'tag1'
   AND    y.tag = 'tag2'
   AND    z.tag = 'tag3'
   );

@ Дэвид уже объяснил, почему ваша третья версия не может функционировать.Но вы никогда не должны использовать его в любом случае, потому что он не может использовать индексы и будет работать на порядок медленнее, чем любой другой метод здесь .

0 голосов
/ 13 марта 2012

В вашем последнем варианте вас не интересует равенство массивов. Вы заинтересованы в содержании массива, HAVING arr содержит все три тега.

И массив1 содержит массив2 - это array1 @> array2 в PostgreSQL.

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

0 голосов
/ 12 марта 2012

Лично мне нравится второй вариант. Но вы должны использовать инструменты PostgreSQL вместе с некоторыми тестовыми запросами, чтобы увидеть, какой из них наиболее эффективен.

Причина, по которой третий не работает как написано, заключается в том, что вам нужно указать порядок сортировки для array_agg (): как заставить array_agg () работать как group_concat () из mySQL

...