我有两个表:“电影”和“用户”。它们之间存在 n:m 关系,描述了用户看过的电影。这是用“看过”的表格来描述的 现在我想为给定的用户找出他没有看过的所有电影。我目前的解决方案是这样的:
SELECT *
FROM movies
WHERE movies.id NOT IN (
SELECT seen.movie_id
FROM seen
WHERE seen.user_id=123
)
这工作正常,但似乎不能很好地扩展。有更好的方法吗?
我有两个表:“电影”和“用户”。它们之间存在 n:m 关系,描述了用户看过的电影。这是用“看过”的表格来描述的 现在我想为给定的用户找出他没有看过的所有电影。我目前的解决方案是这样的:
SELECT *
FROM movies
WHERE movies.id NOT IN (
SELECT seen.movie_id
FROM seen
WHERE seen.user_id=123
)
这工作正常,但似乎不能很好地扩展。有更好的方法吗?
这是不使用您显示的子查询方法的典型方法。这可能会满足@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 (...)
” 好
同样,您必须测试每个解决方案并比较结果以确定。 在不衡量性能的情况下选择任何解决方案都是浪费时间。
您的查询不仅有效,而且是解决问题的正确方法。也许您可以找到解决问题的不同方法?例如,即使对于大型表,外部选择的简单限制也应该非常快。
看到的是您的连接表,所以是的,这看起来是正确的解决方案。您实际上是从 MOVIES 中的全部内容中“减去”SEEN(对于用户)中的电影 ID 集,从而导致该用户看不到的电影。
这被称为“负连接”,遗憾的是 NOT IN 或 NOT EXISTS 是最好的选择。(我希望看到类似于 INNER/OUTER/LEFT/RIGHT 连接的否定连接语法,但其中的 ON 子句可以是减法语句)。
@Bill 没有子查询的解决方案应该可以工作,尽管正如他指出的那样,测试您的解决方案的两种性能是一个好主意。我怀疑子查询与否,整个 SEEN.ID 索引(当然还有整个 MOVIE.ID 索引)将以两种方式进行评估:这将取决于优化器如何从那里处理它。
如果您的 DBMS 支持位图索引,您可以尝试一下。