如何将 IN 条件转换为 INNER JOIN 条件 - 加入速度较慢

Posted

技术标签:

【中文标题】如何将 IN 条件转换为 INNER JOIN 条件 - 加入速度较慢【英文标题】:How to convert IN condition to INNER JOIN condition - join is slower 【发布时间】:2014-03-19 13:38:17 【问题描述】:

我有一个查询在 INNER JOIN 条件下非常慢,但在 WHERE IN 子句中使用时速度更快:

较慢的内连接:

SELECT  * 
FROM cases
left join
(
    select tst.team_set_id 
    from team_sets_teams tst 
    INNER JOIN team_memberships team_memberships 
    ON tst.team_id = team_memberships.team_id
       AND team_memberships.user_id = '1'
       AND team_memberships.deleted=0 group by tst.team_set_id
) cases_tf 
ON cases_tf.team_set_id = cases.team_set_id
LEFT JOIN contacts_cases 
ON contacts_cases.case_id = cases.id 
   AND contacts_cases.deleted = 0
where cases.deleted=0 
ORDER BY cases.name LIMIT 0,20;  

在哪里更快:

SELECT  *
FROM cases 
LEFT JOIN contacts_cases 
ON contacts_cases.case_id = cases.id 
   AND contacts_cases.deleted = 0
where cases.deleted=0 
  and cases.team_set_id in  (
            select tst.team_set_id 
            from team_sets_teams tst
            INNER JOIN team_memberships team_memberships 
            ON tst.team_id = team_memberships.team_id
               AND team_memberships.user_id = '1'
               AND team_memberships.deleted=0 
           group by tst.team_set_id
)
ORDER BY cases.name LIMIT 0,20;

INNER JOIN 和 WHERE IN 子句的解释计划如下:

内连接:

+----+-------------+------------------+------+--------------------------------------------+---------------------+---------+-----------------------------------+--------+----------------------------------------------+
| id | select_type | table            | type | possible_keys                              | key                 | key_len | ref                               | rows   | Extra                                        |
+----+-------------+------------------+------+--------------------------------------------+---------------------+---------+-----------------------------------+--------+----------------------------------------------+
|  1 | PRIMARY     | <derived2>       | ALL  | NULL                                       | NULL                | NULL    | NULL                              |      4 | Using temporary; Using filesort              |
|  1 | PRIMARY     | cases            | ref  | idx_cases_tmst_id                          | idx_cases_tmst_id   | 109     | cases_tf.team_set_id              | 446976 | Using where                                  |
|  1 | PRIMARY     | contacts_cases   | ref  | idx_con_case_case                          | idx_con_case_case   | 111     | sugarcrm.cases.id                 |      1 |                                              |
|  2 | DERIVED     | team_memberships | ref  | idx_team_membership,idx_teammemb_team_user | idx_team_membership | 109     |                                   |      2 | Using where; Using temporary; Using filesort |
|  2 | DERIVED     | tst              | ref  | idx_ud_team_id                             | idx_ud_team_id      | 109     | sugarcrm.team_memberships.team_id |      1 | Using where                                  |
+----+-------------+------------------+------+--------------------------------------------+---------------------+---------+-----------------------------------+--------+----------------------------------------------+

在状态:

------+-----------------------------------+------+----------------------------------------------+
| id | select_type        | table            | type  | possible_keys                              | key                 | key_len | ref                               | rows | Extra                                        |
+----+--------------------+------------------+-------+--------------------------------------------+---------------------+---------+-----------------------------------+------+----------------------------------------------+
|  1 | PRIMARY            | cases            | index | NULL                                       | idx_case_name       | 768     | NULL                              |   20 | Using where                                  |
|  1 | PRIMARY            | contacts_cases   | ref   | idx_con_case_case                          | idx_con_case_case   | 111     | sugarcrm.cases.id                 |    1 |                                              |
|  2 | DEPENDENT SUBQUERY | team_memberships | ref   | idx_team_membership,idx_teammemb_team_user | idx_team_membership | 109     | const                             |    2 | Using where; Using temporary; Using filesort |
|  2 | DEPENDENT SUBQUERY | tst              | ref   | idx_ud_team_id                             | idx_ud_team_id      | 109     | sugarcrm.team_memberships.team_id |    1 | Using where                                  |
+----+--------------------+------------------+-------+--------------------------------------------+---------------------+---------+-----------------------------------+------+----------------------------------------------+

虽然有索引,但我无法弄清楚问题所在。请帮帮我。谢谢。 (这是sugarcrm中的查询)

【问题讨论】:

【参考方案1】:

无法将IN 条件转换为INNER JOIN。 带有IN 条件的查询称为semi-join。 半连接从一个表返回将与另一个表连接但不执行完整连接的行。 下面是一个使用 IN 运算符的半连接查询的简单示例:

SELECT *
FROM table1
WHERE some-column IN (
   SELECT some-other-column
   FROM table2
   WHERE some-conditions
)

上面的半连接可以转化为语义等价的查询 (等效 - 表示giving exactly same results) 使用 EXISTS 运算符和依赖子查询:

SELECT *
FROM table1
WHERE EXISTS(
   SELECT 1
   FROM table2
   WHERE some-conditions
     AND table1.some-column = table2.some-other-column
)

大多数领先的数据库对上述两个查询都使用相同的计划,并且它们的速度是相同的, 不幸的是,mysql 并不总是这样。

联接和半联接是完全不同的查询,具有完全不同的执行计划,因此比较它们的速度就像比较苹果和洋葱。 您可以尝试将第一个带有 IN 的查询转换为带有 EXIST 的查询,但不能转换为联接。

【讨论】:

谢谢,但我们最终完全删除了团队条件。

以上是关于如何将 IN 条件转换为 INNER JOIN 条件 - 加入速度较慢的主要内容,如果未能解决你的问题,请参考以下文章

如何将 SQL Inner Join、Group By 转换为 Linq?

Oracle中Inner join和Where的区别

即使使用 INNER JOIN 而不是 IN,MySQL 查询也非常慢

Oracle中Inner join和Where的区别

使用 LINQ 的 INNER JOIN 3 数据表

INNER JOIN 条件不起作用