SQL查询很慢。没有索引如何改进?

Posted

技术标签:

【中文标题】SQL查询很慢。没有索引如何改进?【英文标题】:SQL query very slow. How to improve without indexes? 【发布时间】:2021-03-03 10:00:47 【问题描述】:

我需要使用单个查询从多个表中获取数据,该查询提供大约 10600 个结果(行)。问题是查询需要很长时间才能执行。就像.. 很长一段时间.. 90 秒。

有什么方法可以在不添加索引的情况下改进查询?表会不断更新(插入、更新、删除的行)。

这里是查询:

SELECT 
    t1.ID
    , t1.ref
    , t1.type
    , GROUP_CONCAT(DISTINCT t3.name) AS parish
    , GROUP_CONCAT(DISTINCT t2.village) AS village
    , GROUP_CONCAT(DISTINCT t2.code) AS code
    , GROUP_CONCAT(DISTINCT t4.year) AS year
FROM table1 t1
    LEFT OUTER JOIN table2 AS t2 ON t2.teade_ID = t1.ID
    LEFT OUTER JOIN table3 AS t3 ON t2.parish_ID = t3.ID
    LEFT OUTER JOIN table4 AS t4 ON t4.teade_ID = t1.ID
GROUP BY t1.ID, t1.ref, t1.type
ORDER BY t1.ID DESC

非常感谢任何帮助!

【问题讨论】:

您确定插入/更新/删除开销对您来说太多了吗?所有4张桌子都是这种情况吗?人们通常会高估这种影响,尤其是在每晚/每周维护索引时。如果您确定,为了得到一个好的答案,您可能需要添加有关相对表大小、列数和类型的信息。 您确定不想使用或添加索引吗?因为这就是构建索引的目的。尤其是当您拥有大量数据时。 GROUP_CONCAT() 字段的输出将是多长时间?如果它们变得很长(对于那些 10600 行),那么您可能会遇到内存问题(没有足够的缓存来将完整的输出保存在内存中,这导致需要临时表......) 如果要进行 JOIN,则需要索引。 知道为什么当我尝试创建索引时会得到结果“mysql 返回了一个空结果集(即零行)。”?空结果集。 【参考方案1】:

计划 A - 使 GROUP BY 和 ORDER BY 匹配:

通常索引主要用于WHERE 子句。但是没有过滤,所以索引可以移动到GROUP BY。你有什么索引?如果您有PRIMARY KEY(id),那么更改为简单的这可能会起作用:

GROUP BY t1.ID
ORDER BY t1.ID DESC

如果ONLY_FULL_GROUP_BY 有问题,您可能需要

GROUP BY t1.ID,      t1.ref,      t1.type
ORDER BY t1.ID DESC, t1.ref DESC, t1.type DESC

无论哪种情况,请注意 GROUP BY 和 ORDER BY 如何相互“匹配”。有了这个(与您所拥有的不同),两个子句都可以一步完成。因此无需收集所有行,进行分组,然后排序。摆脱排序是您提高速度的地方。

B计划——延迟访问麻烦的reftype

SELECT ID, t1x.ref, t1x.type
    FROM (
        SELECT 
            t1.ID
            , GROUP_CONCAT(DISTINCT t3.name) AS parish
            , GROUP_CONCAT(DISTINCT t2.village) AS village
            , GROUP_CONCAT(DISTINCT t2.code) AS code
            , GROUP_CONCAT(DISTINCT t4.year) AS year
        FROM table1 t1
            LEFT OUTER JOIN table2 AS t2 ON t2.teade_ID = t1.ID
            LEFT OUTER JOIN table3 AS t3 ON t2.parish_ID = t3.ID
            LEFT OUTER JOIN table4 AS t4 ON t4.teade_ID = t1.ID
        GROUP BY t1.ID
         ) x
    JOIN t1 AS t1x  USING(ID)
    ORDER BY t1.ID DESC

ORDER BY 在派生表中被忽略; GROUP BY 在外部表中不是必需的。

C 计划 - 假设 ID 是 PK,去掉 GROUP BY:

SELECT  ID, ref, type
        ( SELECT GROUP_CONCAT(DISTINCT t3.name)
                FROM t3 WHERE t3.ID = t1.ID ) AS parish,
        ( ... ) AS ...,
        ( ... ) AS ...,
        ( ... ) AS ...
    FROM t1
    ORDER BY ID DESC

子查询与原始LEFT JOIN 具有相同的语义。

您的原始查询受到“explode-implode”的影响。首先,JOIN 收集所有教区等,形成一个大的中间表。然后分组将其缩小为仅您需要的内容。 C 计划避免了爆炸内爆,因此也避免了 GROUP BY。

此外,不会有排序,因为它可以简单地以相反的顺序扫描表。

【讨论】:

【参考方案2】:

聚合之前加入:

SELECT t1.ID, t1.ref, t1.type,
       t2.villages, t2.codes,
       t3.villages, t4.years
FROM table1 t1 LEFT JOIN
     (SELECT t2.teade_ID, GROUP_CONCAT(t2.code) AS codes,
             GROUP_CONCAT(t2.village) as villages
      FROM table2 t2
      GROUP BY t2.teade_ID
     ) t2
     ON t2.teade_ID = t1.ID LEFT JOIN
     (SELECT t2.teade_ID, GROUP_CONCAT(t3.village) as villages
      FROM table2 t2 JOIN
           table3 t3
           ON t2.parish_ID = t3.ID
      GROUP BY t2.teade_ID
     ) t3
     ON t3.teade_id = t.id LEFT JOIN
     (SELECT GROUP_CONCAT(t4.year) AS year
      FROM table4 t4
      GROUP BY t2.teade_ID
     ) t4
     ON t4.teade_ID = t1.ID
ORDER BY t1.ID DESC;

您可能仍需要在GROUP_CONCAT() 中使用DISTINCT。从您的问题中不清楚是否仍然需要这样做。

为什么这样更快?您的版本正在为每个ID 生成所有表的叉积——可能会大大增加数据的大小。更多数据会使GROUP BY 变慢。

另请注意,外部查询中没有聚合。

【讨论】:

尝试您的查询会给我以下错误:“'group statement' 中的未知列 't2.teade_ID'”即使该列确实存在。知道为什么会这样说吗?

以上是关于SQL查询很慢。没有索引如何改进?的主要内容,如果未能解决你的问题,请参考以下文章

一条sql执行很慢可能的原因,如何优化

Mysql建了索引查询很慢

一个SQL有时执行速度很快有时很慢,请问处理思路

mysql如何找出慢sql

mysql 查询的时候加了索引 查询还是很慢怎么办

mysql全文索引 很慢,速度不如like的百分之一