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计划——延迟访问麻烦的ref
和type
:
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查询很慢。没有索引如何改进?的主要内容,如果未能解决你的问题,请参考以下文章