如何优化这个 sql 查询(内连接)

Posted

技术标签:

【中文标题】如何优化这个 sql 查询(内连接)【英文标题】:How to optimise this sql query (inner joins) 【发布时间】:2015-10-06 17:03:32 【问题描述】:

有没有办法优化这个查询。执行时间超过 40 秒。我尝试了很多方法,比如创建索引。效果不大。仍然查询时间太长。

 SELECT b.id,
   Count(DISTINCT a.id, c.chapters_id) AS pct,
   Count(DISTINCT e.id)                 AS eCount,
   Count(DISTINCT f.id)                 AS fCount,
   Count(DISTINCT d2.id)                 AS d2Count,
   b.NAME,
   e.NAME,
   e.address2,
   f.NAME,
   d2.is_active
FROM   tableA a
       INNER JOIN tableB b
               ON a.modules_id = b.id
       INNER JOIN tableC c
               ON a.modules_id = c.modules_id
       INNER JOIN tableD d1
               ON d1.id = b.store_users_id 
       INNER JOIN tableD d2
               ON d2.id = a.store_users_id
       INNER JOIN tableE e
               ON e.id = d2.stores_id
       INNER JOIN tableF f
               ON e.city = f.id
WHERE  b.type IN( 1, 2, 4 )
       AND b.organizations_id = 156
       AND b.is_enable = true
GROUP  BY b.NAME,b.id

解释

   +----+-------------+-------+--------+-----------------------------------------------------------------------------------------------------+-----------------------------+---------+--------------------------+------+---------------------------------------+
| id | select_type | table | type   | possible_keys                                                                                       | key                         | key_len | ref                      | rows | Extra                                 |
+----+-------------+-------+--------+-----------------------------------------------------------------------------------------------------+-----------------------------+---------+--------------------------+------+---------------------------------------+
|  1 | SIMPLE      | b     | range  | PRIMARY,fk_modules_organizations1,fk_modules_store_users1,sort_modules_name,wh_modules_type,new_idx | new_idx                     | 9       | NULL                     |  160 | Using index condition; Using filesort |
|  1 | SIMPLE      | c     | ref    | fk_module_chapters_modules1                                                                         | fk_module_chapters_modules1 | 4       |         b.id             |    1 | NULL                                  |
|  1 | SIMPLE      | d1    | eq_ref | PRIMARY                                                                                             | PRIMARY                     | 4       |         b.store_users_id |    1 | Using index                           |
|  1 | SIMPLE      | a     | ref    | store_users_id_UNIQUE,fk_user_modules_store_users1,fk_user_modules_modules1                         | fk_user_modules_modules1    | 4       |         b.id             |  149 | NULL                                  |
|  1 | SIMPLE      | d2    | eq_ref | PRIMARY,fk_store_users_stores1                                                                      | PRIMARY                     | 4       |         a.store_users_id |    1 | NULL                                  |
|  1 | SIMPLE      | e     | eq_ref | PRIMARY,Stores-Cities                                                                               | PRIMARY                     | 4       |        d2.stores_id      |    1 | Using where                           |
|  1 | SIMPLE      | f     | eq_ref | PRIMARY                                                                                             | PRIMARY                     | 4       |         e.city           |    1 | NULL                                  |
+----+-------------+-------+--------+-----------------------------------------------------------------------------------------------------+-----------------------------+---------+--------------------------+------+---------------------------------------+

【问题讨论】:

请为您的表提供SHOW CREATE TABLE tablename 语句。这将帮助我们识别任何不正确的数据类型并查看您拥有的索引。此外,IN 总是只有 3 个值,还是会有所不同?这些价值观从何而来? (即用户输入,或以前运行的查询等) 您好,感谢您查看我的查询。 IN 总是只有 3 个值 (1,2,4)。 5 个表的 SHOW CREATE TABLE 详细信息太大了。请告诉我您是否需要为任何特定表显示创建表。谢谢 您没有收到任何回复,因为您没有提供明确的信息。提供所有 SHOW CREATE TABLE 语句,除非您的雇主或类似情况禁止您这样做,否则请更正您的查询以显示实际的表和列名称,而不是这些假名称。它有助于我们理解并降低您在编辑名称时出错的风险。无论如何,我们可以从 *_id 列和 EXPLAIN 语句中的键推断出大部分名称。 【参考方案1】:

这可能会有所帮助:

INDEX(organizations_id, is_enable, type) -- on b

既然你说COUNT(DISTINCT...),闻起来就像JOINs 正在爆炸的行数,那么你DISTINCTifying 他们回到了它们应该在的位置。看到这一点的一种方法是在没有最终 GROUP BY 的情况下执行 SELECT COUNT(*) FROM ... JOIN ...。如果这个数字比任何表大小都大得多,那么您就有了这个经典问题。

有时解决方案是将JOIN 替换为

SELECT  ...,
        ( SELECT COUNT(*) FROM x ... )  AS x_ct,  -- instead of `JOIN x`
        ... 

【讨论】:

以上是关于如何优化这个 sql 查询(内连接)的主要内容,如果未能解决你的问题,请参考以下文章

如何修复这个内连接 SQL 查询?而不是 1 count() 返回 3

如何将带有内连接语句的 Sql 查询转换为带有 Where 语句的 sql 查询(语句中没有内连接)

优化 5 内连接

SQL数据库内表太多,查询一次要半个多小时,如何优化?

lyt经典版MySQL基础——进阶6:连接查询-sql92语法-内连接

将sql代码转换为linq(内连接查询)