使用 INNER JOIN 或 EXISTS 在 m2m 关系中查找属于多个是不是更好?

Posted

技术标签:

【中文标题】使用 INNER JOIN 或 EXISTS 在 m2m 关系中查找属于多个是不是更好?【英文标题】:Is it better to use INNER JOIN or EXISTS to find belonging to several in m2m relation?使用 INNER JOIN 或 EXISTS 在 m2m 关系中查找属于多个是否更好? 【发布时间】:2012-10-15 08:27:25 【问题描述】:

给定 m2m 关系:items-categories 我有三个表:

项目类别items_categories 包含对两者的引用

我想找到属于所有给定类别集的项目:

Find Item 
belonging to a category in [1,3,6] 
and belonging to a category in [7,8,4] 
and belonging to a category in [12,66,42]
and ...

我可以想到两种在 mysql 中完成此任务的方法。

选项 A:内部联接:

SELECT id from items 
INNER JOIN category c1 ON (item.id = c1.item_id)
INNER JOIN category c2 ON (item.id = c2.item_id)
INNER JOIN category c3 ON (item.id = c3.item_id)
...
WHERE
c1.category_id IN [1,3,6] AND
c2.category_id IN [7,8,4] AND
c3.category_id IN [12,66,42] AND
...;

选项 B:存在:

SELECT id from items
WHERE
EXISTS(SELECT category_id FROM category WHERE category.item_id = id AND category_id in [1,3,6] AND
EXISTS(SELECT category_id FROM category WHERE category.item_id = id AND category_id in [7,8,4] AND
EXISTS(SELECT category_id FROM category WHERE category.item_id = id AND category_id in [12,66,42] AND
...;

这两个选项都有效。问题是:对于大型项目表来说,哪个是最快/最优化的? 或者我缺少一个 OPTION C?

【问题讨论】:

使用EXPLAIN查看每个查询的执行计划。 选项 A 和选项 B 可能不会产生相同的结果。 好东西,伙计们!谢谢大家的帮助和cmets^^ 【参考方案1】:

选项 A

JOINEXIST 有优势,因为它可以更有效地使用索引,尤其是在大表的情况下

【讨论】:

【参考方案2】:

一般来说,JOIN 的效率更高。

但是,需要注意的一件事是,连接可能会在您的输出中产生重复的行。例如,如果项目 id 在类别 1 和 3 中,第一个 JOIN 将导致两行 id 123。如果项目 id 999 在类别 1、3、7、8、12 和 66 中,您将得到 行 999 在您的结果中 (2*2*2)。

重复行是您需要注意和处理的事情。在这种情况下,您可以只使用select distinct id...。但是,通过复杂的查询消除重复项可能会变得更加复杂。

【讨论】:

谢谢!在原始查询中,我使用的是 distinct。如果我处理 100.000 多个项目,distinct 会在性能方面产生重大影响吗? @RomanSemko,distinct 会影响性能,但可能不会有太大的不同。 我正在使用内部连接转换exists。我现在需要消除重复的结果,所以我尝试使用 distinct 或 group by。在我的情况下,性能差异要差得多【参考方案3】:
 select distinct `user_posts_id` from `user_posts_boxes`
     where `user_id` = 5 
     and 
     exists (select * from `box` where `user_posts_boxes`.`box_id` = `box`.`id` 
     and `status` in ("A","F"))
     order by `user_posts_id` desc limit 200;



 select distinct `user_posts_id` from `user_posts_boxes`
 INNER JOIN box on box.id = `user_posts_boxes`.`box_id` and box.`status` in ("A","F")
 and box.user_id = 5
 order by `user_posts_id` desc limit 200

我尝试了这两个查询,但上面的查询对我来说工作得更快。两个表都有大数据集。几乎“user_posts_boxes”有 400 万个,boxes 是 150 万个。

第一次查询耗时 = 0.147 毫秒 第二次查询几乎 = 0.5 到 0.9 MS

但是我的数据库表是inno db,并且也应用了物理关系。

所以我应该选择存在,但这也取决于您的数据库结构。

【讨论】:

【参考方案4】:

您正在选项 A 中使用 Join 和 选项 B 中的 subquery。区别在于:

在大多数情况下,JOIN 比子查询更快,而且子查询更快的情况很少见。

在 JOIN 中,RDBMS 可以创建一个更适合您的查询的执行计划,并且可以预测应该加载哪些数据以进行处理并节省时间,这与子查询不同,子查询将运行所有查询并将所有数据加载到进行处理。

子查询的好处是它们比 JOIN 更具可读性:这就是大多数 SQL 新手更喜欢它们的原因;这是简单的方法;但是在性能方面,JOINS 在大多数情况下会更好,即使它们也不难阅读。

【讨论】:

与 JOIN 相比,大多数现代优化器(MySQL 不是)将为等效的子查询解决方案生成相同的执行计划。但是 JOIN 不一定等同于子查询解决方案(正如 dan1111 指出的那样)【参考方案5】:

另外,请阅读 Mysql 文档中的 this,其中讨论了这一点和以前版本中的新变化以及 MySql 如何实际执行 exists

如果子查询满足上述条件,MySQL 会将其转换为半连接(或者,在 MySQL 8.0.17 或更高版本中,如果适用,则为反连接)并从这些策略中做出基于成本的选择:

将子查询转换为连接,或使用表拉出并运行查询 作为子查询表和外部表之间的内部连接。桌子 pullout 从子查询中拉出一个表到外部查询。

...

【讨论】:

以上是关于使用 INNER JOIN 或 EXISTS 在 m2m 关系中查找属于多个是不是更好?的主要内容,如果未能解决你的问题,请参考以下文章

使用 INNER JOIN 和 NOT EXISTS 访问 INSERT 语句给出错误结果

Inner Join vs Exists() 同时避免重复行

INEXISTS的相关子查询用INNER JOIN 代替--性能优化

SQL中inner join,outer join和cross join的区别

在 SQL ACCESS 中使用 LEFT 或 INNER JOIN 和 WHERE 的问题

如何在 R 中 dplyr::inner_join 多个 tbls 或 data.frames