Запрос на поиск сообщений с точным набором тегов (отношение «многие ко многим») - PullRequest
2 голосов
/ 26 января 2012

Предположим, у меня есть следующие три таблицы, выражающие отношение, в котором сообщениям присваиваются теги (отношение многие ко многим):

create table posts (id integer, content text, primary key (id));
create table tags (tag varchar(30), description text, primary key (tag));
create table post_tags (post_id integer, tag varchar(10),
    primary key (post_id, tag),
    foreign key (post_id) references posts (id),
    foreign key (tag) references tags (tag));

Теперь предположим, что я хочу найти все сообщения с точно такими же тегамиумный, интересный} и никаких других.

Вот моя неудачная попытка.Он находит посты с тегами {умный, интересный}, но также находит посты с тегами {умный, интересный, раздражающий} или {умный, интересный, неэтичный}.

select t1.post_id from post_tags as t1
    inner join post_tags as t2 on t2.post_id=t1.post_id
    where t1.tag='clever' and t2.tag='interesting';

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

Ответы [ 2 ]

3 голосов
/ 26 января 2012

Это точная проблема реляционного деления.

В SQL Server хорошо работающий метод (при условии уникального ограничения на post_id,tag) равен

SELECT post_id
FROM   post_tags
GROUP  BY post_id
HAVING MIN(CASE
             WHEN Keyword IN ( 'clever', 'interesting' ) THEN 1
             ELSE 0
           END) = 1
       AND SUM(CASE
                 WHEN Keyword IN ( 'clever', 'interesting' ) THEN 1
                 ELSE 0
               END) = 2  

ТакЯ бы не исключил идею использования GROUP_CONCAT в HAVING вместо

HAVING GROUP_CONCAT(DISTINCT Keyword ORDER BY Keyword) = 'clever,interesting'
2 голосов
/ 26 января 2012

Должно быть в состоянии сделать это использование NOT EXISTS, например,

select t1.post_id
from post_tags as t1
    inner join post_tags as t2 on t2.post_id = t1.post_id
where 
    t1.tag = 'clever' 
and t2.tag = 'interesting' 
and not exists (
    select *
    from post_tags t3
    where 
        t3.tag not in ('clever', 'interesting')
    and t3.post_id = t1.post_id
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...