【问题标题】: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^^

标签: mysql sql performance many-to-many


【解决方案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 从子查询中拉出一个表到外部查询。

      ...

      【讨论】:

        猜你喜欢
        • 2011-01-11
        • 2016-05-18
        • 2016-08-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-13
        • 2013-02-27
        相关资源
        最近更新 更多