【问题标题】:join a many to many table using multiple conditions使用多个条件连接多对多表
【发布时间】:2020-01-17 17:34:32
【问题描述】:

我正在使用 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)
);

假设标签 id 4 是“新闻”,标签 id 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);

有没有更好的连接类型或其他方式来形成这个查询?

【问题讨论】:

    标签: sql sqlite join many-to-many


    【解决方案1】:

    您的尝试很好(留下了几个拼写错误):您可以使用多个 joins 或带有子查询的 in 条件。

    另一种方法非常接近in 技术,对每个标签ID 使用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
        )
    

    对于所有这些查询(joins、inexists),您确实需要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
    

    这更容易修改以考虑更多标签,但是您需要根据您的真实数据评估此解决方案的性能;在大型数据集上聚合往往会减慢速度。

    【讨论】:

      【解决方案2】:

      您可以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 子句中仅更改它们的编号。

      【讨论】:

        猜你喜欢
        • 2022-01-08
        • 2012-10-20
        • 2016-03-11
        • 1970-01-01
        • 2019-01-15
        • 2017-08-18
        • 2023-03-16
        • 2014-11-07
        相关资源
        最近更新 更多