2009-02-12 101 views
18

我有两个表:'电影'和'用户'。 这些人之间存在n:m的关系,描述了用户看过的电影。这是描述了一个表'看' 现在我想找出一个给定的用户,所有他没有看到的电影。 我目前的解决办法是这样的:MySQL:查找不参与关系的行

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

这工作得很好,但似乎并没有很好地进行缩放。有没有更好的方法呢?

+0

如果它不能很好地缩放,那么您的索引无效。你的索引是什么? – dkretz 2009-02-13 00:08:13

+0

>这工作正常,但似乎没有很好地扩展。有没有更好的方法呢?您是否在此查询上尝试了EXPLAIN? – VolkerK 2009-02-13 00:03:28

回答

27

下面是一个典型的方法来做这个查询,而不使用你展示的子查询方法。这可以满足@ 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 (...)”更好

再次,您必须测试每个解决方案并比较结果以确保。 如果不测量性能,选择任何解决方案都是浪费时间。

4

不仅您的查询能够正常工作,而且正确地处理了所述问题。也许你可以找到一种不同的方式来解决这个问题?例如,即使对于大型表格,外部选择的简单限制也应该非常快。

4

看到是你的连接表,所以是的,这看起来像是正确的解决方案。您实际上是从电影中的整体中“减去”SEEN中的一组电影ID(对于用户),导致该用户看不见的电影。

这被称为“否定连接”,可悲的是不在或不存在是最好的选择。 (我希望看到一个类似于INNER/OUTER/LEFT/RIGHT连接的负连接语法,但是ON子句可以是一个减法语句)。

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

0

如果你的DBMS支持位图索引,你可以试试它们。

+0

他标记了问题'mysql'。 MySQL不支持位图索引。 – 2009-02-13 00:08:22