【问题标题】:Correctly join multiple many-to-many tables - MySQL query正确连接多个多对多表 - MySQL 查询
【发布时间】:2020-04-23 00:29:03
【问题描述】:

一个看似通用的 SQL 查询真的让我一无所知。 情况就是这样。 我有 3 个通用表格(此处为简化版):

Movie

id | title
-----------------------
1  | Evil Dead
-----------------------
2  | Bohemian Rhapsody
....

Genre

id | title
-----------------------
1  | Horror
-----------------------
2  | Comedy
....


Rating

id | title
-----------------------
1  | PG-13
-----------------------
2  | R
....

还有 2 个多对多表来连接它们:

Movie_Genre

movie_id | genre_id


Movie_Rating

movie_id | rating_id

最初的挑战是编写一个查询,允许我获取属于多种类型的电影(例如恐怖喜剧或科幻动作)。

谢天谢地,我能在这里找到这个解决方案 MySQL: Select records where joined table matches ALL values

但是,获取属于多个多对多表的记录的正确选项是什么?例如。 R级恐怖喜剧。有没有办法在没有子查询(或只有一个)的情况下这样做?

【问题讨论】:

  • 为什么会有两个多对多表?

标签: mysql sql join subquery having-clause


【解决方案1】:

一种方法使用相关子查询:

select m.*
from movies m
where (select count(*)
       from movie_genre mg
       where mg.movie_id = m.id
      ) > 1 and
      (select count(*)
       from movie_rating mr
       where mr.movie_id = m.id
      ) > 1 ;

使用movie_genre(movie_id)movie_rating(movie_id) 上的索引,这可能具有相当合理的性能。

以上可能是最有效的方法。但是,如果您想避免子查询,一种方法是:

select mg.movie_id
from movie_genres mg join
     movie_ratings mr
     on mg.movie_id = mr.movie_id
group by mg.movie_id
having count(distinct mg.genre_id) > 0 and
       count(distinct mr.genre_id) > 0;

比上面更高效的是在join之前聚合:

select mg.movie_id
from (select movie_id
      from mg_genres
      group by movie_id
      having count(*) >= 2
     ) mg join
     (select movie_id
      from mg_ratings
      group by movie_id
      having count(*) >= 2
     ) mr
     on mg.movie_id = mr.movie_id;

虽然您声明要避免子查询,但具有讽刺意味的是,没有子查询的版本可能是这三个选项中性能最差的。

【讨论】:

  • 感谢最新版本!它也很适合代码!
【解决方案2】:

例如R级恐怖喜剧

您可以join 将所有表放在一起,按电影聚合并使用HAVING 子句进行过滤:

select m.id, m.title
from movies m
inner join movie_genre  mg on mg.movid_id = m.id
inner join genre g on g.id = mg.genre_id
inner join movie_rating mr on mr.movie_id = m.id
inner join rating r on r.id = mr.rating_id
group by m.id, m.title
having 
    max(r.title = 'R') = 1 
    and max(g.title = 'Horror') = 1
    and max(g.title = 'Comedy') = 1

您还可以使用几个exists 条件以及相关的子查询:

select m.*
from movie m
where
    exists (
        select 1 
        from movie_genre mg
        inner join genre g on g.id = mg.genre_id
        where mg.movie_id = m.id and g.title = 'R')
    and exists (
        select 1
        from movie_rating mr
        inner join rating r on r.id = mr.rating_id
        where mr.movie_id = m.id and r.title = 'Horror'
    )
    and exists (
        select 1
        from movie_rating mr
        inner join rating r on r.id = mr.rating_id
        where mr.movie_id = m.id and r.title = 'Comedy'
    )

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-11-06
    • 2020-03-26
    • 2013-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多