31

我有两个表:“电影”和“用户”。它们之间存在 n:m 关系,描述了用户看过的电影。这是用“看过”的表格来描述的 现在我想为给定的用户找出他没有看过的所有电影。我目前的解决方案是这样的:

SELECT *
FROM movies 
WHERE movies.id NOT IN (
     SELECT seen.movie_id 
     FROM seen 
     WHERE seen.user_id=123
)

这工作正常,但似乎不能很好地扩展。有更好的方法吗?

4

4 回答 4

45

这是不使用您显示的子查询方法的典型方法。这可能会满足@Godeke 要求查看基于联接的解决方案。

SELECT * 
FROM movies m
 LEFT OUTER JOIN seen s
 ON (m.id = s.movie_id AND s.user_id = 123)
WHERE s.movie_id IS NULL;

但是,在大多数品牌的数据库中,此解决方案的性能可能比子查询解决方案差。最好使用 EXPLAIN 来分析这两个查询,看看哪一个会根据您的架构和数据做得更好。

这是子查询解决方案的另一个变体:

SELECT * 
FROM movies m
WHERE NOT EXISTS (SELECT * FROM seen s 
                  WHERE s.movie_id = m.id 
                    AND s.user_id=123);

这是一个相关子查询,必须针对外部查询的每一行进行评估。通常这很昂贵,并且您的原始示例查询更好。另一方面,在 MySQL 中,“ NOT EXISTS”往往比“ column NOT IN (...)” 好

同样,您必须测试每个解决方案并比较结果以确定。 在不衡量性能的情况下选择任何解决方案都是浪费时间。

于 2009-02-13T00:05:51.890 回答
4

您的查询不仅有效,而且是解决问题的正确方法。也许您可以找到解决问题的不同方法?例如,即使对于大型表,外部选择的简单限制也应该非常快。

于 2009-02-12T23:54:59.530 回答
4

看到的是您的连接表,所以是的,这看起来是正确的解决方案。您实际上是从 MOVIES 中的全部内容中“减去”SEEN(对于用户)中的电影 ID 集,从而导致该用户看不到的电影。

这被称为“负连接”,遗憾的是 NOT IN 或 NOT EXISTS 是最好的选择。(我希望看到类似于 INNER/OUTER/LEFT/RIGHT 连接的否定连接语法,但其中的 ON 子句可以是减法语句)。

@Bill 没有子查询的解决方案应该可以工作,尽管正如他指出的那样,测试您的解决方案的两种性能是一个好主意。我怀疑子查询与否,整个 SEEN.ID 索引(当然还有整个 MOVIE.ID 索引)将以两种方式进行评估:这将取决于优化器如何从那里处理它。

于 2009-02-12T23:55:18.297 回答
0

如果您的 DBMS 支持位图索引,您可以尝试一下。

于 2009-02-13T00:02:17.920 回答