关于mysql:使用INNER JOIN或EXISTS来查找属于m2m关系的几个更好吗?

Is it better to use INNER JOIN or EXISTS to find belonging to several in m2m relation?

给定的对象-关系M2M:在桌子有三类:

  • 项目
  • 鸭类
  • _类的项目,两个队都证明人

我想找个项目属于给定类:全套

1
2
3
4
5
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 ...

有两种方式,我觉得在这两个accomplish MySQL。

选项A:内部连接:

1
2
3
4
5
6
7
8
9
10
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:exists:

1
2
3
4
5
6
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
...;

两个期权的工作。问题是:"这是fastest最佳/最大的表项呢?或是有一个选项C am missing?


一般来说,JOIN更有效。

但是,要注意的一点是,联接可以在输出中生成重复的行。例如,如果项目ID在类别1和类别3中,则第一个JOIN将导致ID 123有两行。如果项目ID 999属于类别1、3、7、8、12和66,则结果中的999将有八行(2*2*2)。

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


方案A

EXIST相比,JOIN具有优势,因为它将更有效地使用索引,特别是在大型表的情况下。


您在选项A中使用联接,在选项B中使用子查询。区别在于:

在大多数情况下,联接比子查询更快,而且子查询更快是非常罕见的。

在joins中,RDBMS可以创建一个更适合查询的执行计划,可以预测应该加载哪些数据以进行处理并节省时间,而不像子查询那样,它将运行所有查询并加载所有数据以进行处理。

子查询的好处在于,它们比联接更可读:这就是大多数新的SQL用户更喜欢它们的原因;这是一种简单的方法;但在性能方面,联接在大多数情况下都更好,即使它们并不难读取。


1
2
3
4
5
6
7
8
9
10
11
12
13
 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

我尝试了这两个查询,但上面的查询对我来说更快。两个表都有大的数据集。几乎"用户邮箱"有400万个,邮箱150万个。

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

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

所以我应该选择exists,但它也取决于你如何拥有你的db结构。