SQL 查询很慢。如何在没有索引的情况下改进?

时间:2021-03-03 10:00:47

标签: mysql sql performance

我需要使用一个查询从多个表中获取数据,该查询提供大约 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

非常感谢任何帮助!

2 个答案:

答案 0 :(得分:0)

加入前聚合:

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;

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

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

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

答案 1 :(得分:0)

计划 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。

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