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