【问题标题】:SQL many-to-many filter out items rather than includeSQL 多对多过滤掉项目而不是包含
【发布时间】:2021-03-02 07:22:54
【问题描述】:

所以我正在构建一个应用程序,其中一个元素是搜索电影。数据存储在关系数据库中。

过滤条件之一是流派。电影可以有多种类型,所以它是多对多的关系。除了通常的标签示例之外,过滤器的工作方式并不完全相同。

假设您有一部类型为 documentarysports 的纪录片。如果我对纪录片感兴趣但对体育不感兴趣,我很可能对体育相关的纪录片不感兴趣。如果我像通常使用标签一样实现查询,则查询如下所示:

select distinct(m.id) from movies m
  join genres_movies gm.movie_id = m.id
  join genres on gm.genre_id = g.id
  where genre.name in ('documentary', 'action', 'horror')

通过这个查询,我会得到上面提到的体育纪录片。那么如何排除所有具有某种类型的电影呢?

【问题讨论】:

    标签: sql many-to-many


    【解决方案1】:

    我认为存在和“不存在”是你需要的

    select distinct(m.id) from movies m
     where exists (select 1 
                     from genres g 
                     join genres_movies gm 
                       on gm.genre_id = g.id
                    where gm.movie_id = m.id
                      and name in ('documentary', 'action', 'horror'))
       and not exists (select 1 
                         from genres g 
                         join genres_movies gm  
                           on gm.genre_id = g.id
                        where gm.movie_id = m.id
                          and g.name not in ('sports', 'drama'))
    

    【讨论】:

    • 不确定这是否可行。它仍然会返回将电影加入类型纪录片的那一行
    • @leifg 我已经更新了答案。以前的版本一团糟
    【解决方案2】:

    我喜欢对这类问题使用聚合:

    select gm.movie_id
    from genres_movies gm join
         genres g
         on gm.genre_id = g.id
    group by gm.movie_id
    having sum(case when g.name in ('documentary', 'action', 'horror') then 1 else 0 end) > 0 and
           sum(case when g.name = 'sports' then 1 else 0 end) = 0;
    

    > 0 表示至少分配了其中一种类型。 = 0 表示没有分配这些类型。

    因为您的代码只选择了电影 id,所以我删除了对 movies 的加入。当然,如果您需要该表中的其他列,则将其放回 int。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-29
      • 2021-05-06
      • 2021-01-08
      • 2020-02-13
      • 1970-01-01
      • 2022-11-02
      相关资源
      最近更新 更多