присоединиться к таблице «многие ко многим», используя несколько условий - PullRequest
1 голос
/ 17 января 2020

Я использую sqlite3 и пытаюсь выбрать все статьи, в которых есть какой-либо из (или всех) заданных тегов.

CREATE TABLE article (
    id INTEGER NOT NULL,
    title TEXT NOT NULL,
    PRIMARY KEY (id),
    UNIQUE (title)
);
CREATE TABLE tag (
    id INTEGER NOT NULL,
    name TEXT NOT NULL,
    PRIMARY KEY (id),
    UNIQUE (name)
);
CREATE TABLE drill_to_tag (
    tag_id INTEGER NOT NULL,
    article_id INTEGER NOT NULL,
    PRIMARY KEY (tag_id, article_id),
    FOREIGN KEY(tag_id) REFERENCES tag (id),
    FOREIGN KEY(article_id) REFERENCES article (id)
);

Допустим, идентификатор тега 4 - это «новости», идентификатор тега - 5 это «европа», а тег id 6 - это «США».

Я могу получить статьи с тегом id 4, используя:

select a.title from article a
  inner join article_to_tag
    on a.id = article_to_tag.article_id
    where article_to_tag.tag_id = 4;

Но что я действительно хочу, так это способ получить статьи появляются в таблице «многие ко многим» с обоими тегами 4 и 5 - европейские новости.

Этот запрос делает это, но кажется уродливым?

select a.id, a.title from article a
  inner join article_to_tag atag1
    on a.id = atag1.article_id
  inner join article_to_tag atag2
    on a.id = atag2.article_id
  where atag1.tag_id = 4 and atag2.tag_id = 5;

И этот кажется еще более уродливым.

select a.id, a.title from article a
  where
    a.id in (select article_id from article_to_tag where article_id = 4)
      and
    a.id in (select article_id from article_to_tag where article_id = 5);

Есть ли лучший тип объединения или какой-либо другой способ сформировать этот запрос?

Ответы [ 2 ]

1 голос
/ 17 января 2020

Ваши попытки в порядке (несколько опечаток оставлены в стороне): вы можете использовать несколько join s или in условий с подзапросами.

Еще один метод, который довольно близок к in техника, использует условие exists для каждого идентификатора тега:

select a.*
from article a
where 
    exists (
        select 1 from article_to_tag at where at.article_id = a.article_id and at.tag_id = 4
    ) and exists (
        select 1 from article_to_tag at where at.article_id = a.article_id and at.tag_id = 5
    )

Для всех этих запросов (join s, in, exists) вам нужен индекс для article_to_tag(article_id, tag_id).

Более кратким способом является использование агрегации и фильтрации с предложением having:

select a.id, a.title
from article a
inner join article_tag at on at.article_id = a.article_id
where at.tag_id in (4, 5)
group by a.id, a.title
having count(ditinct at.tag_id) = 2

Это легче изменить, чтобы учесть больше тегов, однако вам потребуется оценить эффективность этого решения по вашим реальным данным; агрегация имеет тенденцию замедляться на больших датах.

0 голосов
/ 17 января 2020

Вы можете group by a.title и установить условие в предложении HAVING:

select a.title 
from article a inner join article_to_tag t
on a.id = t.article_id
where t.tag_id in (4, 5)
group by a.title
having count(distinct t.tag_id) = 2

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

...