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:查找不参与关系的行的主要内容,如果未能解决你的问题,请参考以下文章