MySQL:查找不参与关系的行

Posted

技术标签:

【中文标题】MySQL:查找不参与关系的行【英文标题】:MySQL: Finding rows that don't take part in a relationship 【发布时间】:2010-10-07 08:14:47 【问题描述】:

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

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

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

【问题讨论】:

> 这工作正常,但似乎不能很好地扩展。有更好的方法吗?您是否在此查询中尝试过 dev.mysql.com/doc/refman/5.0/en/…>? 如果它不能很好地缩放,那么你的索引是无效的。你的索引是什么? 【参考方案1】:

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

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

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

【讨论】:

【参考方案2】:

这是执行此查询的典型方法,无需使用您展示的子查询方法。这可能会满足@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 (...)

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

【讨论】:

我只是一直忘记这个OUTER JOIN 把戏。谢谢!【参考方案3】:

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

【讨论】:

他将问题标记为“mysql”。 MySQL 不支持位图索引。 糟糕,我没有看标签。 :(【参考方案4】:

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

【讨论】:

以上是关于MySQL:查找不参与关系的行的主要内容,如果未能解决你的问题,请参考以下文章

面试官:MySQL 如何查找删除重复行?我竟然写不出来。。

面试官:MySQL 如何查找删除重复行?我竟然写不出来。。

查找一列中具有相同值而另一列中具有其他值的行?

通过 id mysql 查找具有最新日期的行

mysql:查找具有重复值和条件的行[重复]

用于查找一个表中但不在另一个表中的行的 MySQL 查询