使用 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
JOIN
比 EXIST
有优势,因为它可以更有效地使用索引,尤其是在大表的情况下
【讨论】:
【参考方案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的区别