为 group_concat 函数优化 MySQL 查询
Optimize MySQL query for group_concat function
SELECT SQL_NO_CACHE link.stop, stop.common_name, locality.name, stop.bearing, stop.latitude, stop.longitude
FROM service
JOIN pattern ON pattern.service = service.code
JOIN link ON link.section = pattern.section
JOIN naptan.stop ON stop.atco_code = link.stop
JOIN naptan.locality ON locality.code = stop.nptg_locality_ref
GROUP BY link.stop
上述查询大约需要 800 毫秒 - 1000 毫秒到 运行。
如果我附加一个 group_concat
语句,则查询需要 8 - 10 秒:
SELECT SQL_NO_CACHE link.stop, link.stop, stop.common_name, locality.name, stop.bearing, stop.latitude, stop.longitude, group_concat(service.line) lines
如何更改此查询,以便 运行 使用 group_concat
语句在 2 秒内完成?
SQL Fiddle: http://sqlfiddle.com/#!9/414fe
EXPLAIN
两个查询的语句:http://i.imgur.com/qrURgzV.png
备注:您正在使用nonstandard MySQL extension to GROUP BY。它恰好适合您,因为 link.stop
连接到 stop.atco_code
,它本身就是一个主键。但是你需要非常小心。
我建议你添加一些复合索引。您在 service
加入 pattern
,并在 section
加入。所以添加这个索引。
ALTER TABLE pattern ADD INDEX service_section (service, section, line);
这将使查询仅使用索引,而不必点击 table 本身来检索 JOIN 或您的 GROUP_CONCAT()
操作所需的信息。 (您也可以删除 service
上的索引,这个新索引使其变得多余)。
同样,您想在 link
table 上创建一个索引 (section, stop)
,并删除 section
.
上的索引
在 stop
上,您正在使用大部分列,并且您在 atco_code
上已经有一个索引 (PK),所以就让这个吧。
最后,在 locality
上为 (code,name)
建立索引。
所有这些索引猴子业务应该减少 MySQL 为满足您的查询而必须做的工作量。
现在看,一旦将 WHERE anything = anything
添加到查询中,您可能需要向这些索引中的一个或多个添加一列。您绝对应该阅读 multi-column indexing and grouping;良好的索引是您的数据类型成功的关键因素。
在插入大量行后,您还应该在每个 table 上 运行 ANALYZE TABLE xxxx
,以确保查询优化器可以看到有关 table 和索引。
这个查询需要多长时间?
SELECT p.section, GROUP_CONCAT(s.line)
FROM pattern p join
service s
ON p.service = s.code
GROUP BY p.section
我认为您可以在子查询中执行 group_concat()
,因此外部查询不需要聚合。当子查询中有一个 table 时,这可以加快查询速度。在你的情况下,有两个。
最终结果会是这样的:
link.section = pattern.section
SELECT SQL_NO_CACHE . . .,
(SELECT GROUP_CONCAT(s.line)
FROM pattern p join
service s
ON p.service = s.code
WHERE p.section = link.section
) as lines
FROM link JOIN
naptan.stop
ON stop.atco_code = link.stop JOIN
naptan.locality
ON locality.code = stop.nptg_locality_ref;
对于此查询,您需要以下附加索引:pattern(section, service)
和 service(code, line)
。
我不知道这是否有效,但值得一试。
注意:这是假设您确实不需要 group by
其余的列。
SELECT SQL_NO_CACHE link.stop, stop.common_name, locality.name, stop.bearing, stop.latitude, stop.longitude
FROM service
JOIN pattern ON pattern.service = service.code
JOIN link ON link.section = pattern.section
JOIN naptan.stop ON stop.atco_code = link.stop
JOIN naptan.locality ON locality.code = stop.nptg_locality_ref
GROUP BY link.stop
上述查询大约需要 800 毫秒 - 1000 毫秒到 运行。
如果我附加一个 group_concat
语句,则查询需要 8 - 10 秒:
SELECT SQL_NO_CACHE link.stop, link.stop, stop.common_name, locality.name, stop.bearing, stop.latitude, stop.longitude, group_concat(service.line) lines
如何更改此查询,以便 运行 使用 group_concat
语句在 2 秒内完成?
SQL Fiddle: http://sqlfiddle.com/#!9/414fe
EXPLAIN
两个查询的语句:http://i.imgur.com/qrURgzV.png
备注:您正在使用nonstandard MySQL extension to GROUP BY。它恰好适合您,因为 link.stop
连接到 stop.atco_code
,它本身就是一个主键。但是你需要非常小心。
我建议你添加一些复合索引。您在 service
加入 pattern
,并在 section
加入。所以添加这个索引。
ALTER TABLE pattern ADD INDEX service_section (service, section, line);
这将使查询仅使用索引,而不必点击 table 本身来检索 JOIN 或您的 GROUP_CONCAT()
操作所需的信息。 (您也可以删除 service
上的索引,这个新索引使其变得多余)。
同样,您想在 link
table 上创建一个索引 (section, stop)
,并删除 section
.
在 stop
上,您正在使用大部分列,并且您在 atco_code
上已经有一个索引 (PK),所以就让这个吧。
最后,在 locality
上为 (code,name)
建立索引。
所有这些索引猴子业务应该减少 MySQL 为满足您的查询而必须做的工作量。
现在看,一旦将 WHERE anything = anything
添加到查询中,您可能需要向这些索引中的一个或多个添加一列。您绝对应该阅读 multi-column indexing and grouping;良好的索引是您的数据类型成功的关键因素。
在插入大量行后,您还应该在每个 table 上 运行 ANALYZE TABLE xxxx
,以确保查询优化器可以看到有关 table 和索引。
这个查询需要多长时间?
SELECT p.section, GROUP_CONCAT(s.line)
FROM pattern p join
service s
ON p.service = s.code
GROUP BY p.section
我认为您可以在子查询中执行 group_concat()
,因此外部查询不需要聚合。当子查询中有一个 table 时,这可以加快查询速度。在你的情况下,有两个。
最终结果会是这样的:
link.section = pattern.section
SELECT SQL_NO_CACHE . . .,
(SELECT GROUP_CONCAT(s.line)
FROM pattern p join
service s
ON p.service = s.code
WHERE p.section = link.section
) as lines
FROM link JOIN
naptan.stop
ON stop.atco_code = link.stop JOIN
naptan.locality
ON locality.code = stop.nptg_locality_ref;
对于此查询,您需要以下附加索引:pattern(section, service)
和 service(code, line)
。
我不知道这是否有效,但值得一试。
注意:这是假设您确实不需要 group by
其余的列。