【问题标题】:Optimize MySQL query for group_concat function优化 MySQL 查询 group_concat 函数
【发布时间】:2015-05-23 20:53:35
【问题描述】:
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 小提琴:http://sqlfiddle.com/#!9/414fe

EXPLAIN 两个查询的语句:http://i.imgur.com/qrURgzV.png

【问题讨论】:

  • 您能否发布EXPLAIN 的查询结果? (我注意到你的一张表在 MyISAM 中并且有一个地理空间索引。)你可能想读这个:use-the-index-luke.com
  • @OllieJones 嗨 Ollie,我在问题中添加了 EXPLAIN,很抱歉没有提及 MyISAM 表
  • 在我看来,粗略的估计,在使用 GROUP_CONCAT 解决方案的 SQL 中没有问题。
  • 当您优化 GROUP BY 查询时,结果集中的所有列都是相关的。这是因为复合索引可以产生很大的性能差异。
  • @OllieJones 根据这些信息,我确保所有选定的字段现在都在查询中

标签: mysql sql innodb myisam


【解决方案1】:

这个查询需要多长时间?

SELECT p.section, GROUP_CONCAT(s.line)
FROM pattern p join
     service s
     ON p.service = s.code
GROUP BY p.section

我认为您可以在子查询中执行group_concat(),因此外部查询不需要聚合。当子查询中有一个表时,这可以加快查询速度。就你而言,有两个。

最终的结果会是这样的:

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 来显示其余列。

【讨论】:

  • 您输入的第一个查询需要 0.064 秒。整个查询平均需要大约 1.5 秒!非常感谢:-)
  • @jskidd3 。 . .凉爽的。我不喜欢 MySQL 不会有效地为group by 使用索引的事实(可能在将来的某个版本中)。我希望它实现了你的原始版本,就像上面写的一样,但我很高兴这对你有用。
【解决方案2】:

备注:您使用的是nonstandard MySQL extension to GROUP BY。它恰好对你有用,因为link.stopstop.atco_code 相连,stop.atco_code 本身就是一个主键。但是你需要非常小心。

我建议你添加一些复合索引。您在service 上加入pattern,并在section 上加入。所以添加这个索引。

ALTER TABLE pattern ADD INDEX service_section (service, section, line);

这将使查询只使用索引,而不必访问表本身来检索 JOIN 或您的 GROUP_CONCAT() 操作所需的信息。 (您也可以只删除 service 上的索引,这个新索引使其变得多余)。

同样,您想在link 表上创建索引(section, stop),并删除仅在section 上的索引。

stop 上,您使用了大部分列,并且您已经在atco_code 上拥有了一个索引 (PK),所以让这个成为吧。

最后,在locality 上为(code,name) 放置一个索引。

所有这些索引猴子业务都应该减少 MySQL 为满足您的查询而必须做的工作量。

现在看,一旦您将WHERE anything = anything 添加到查询中,您可能需要将一列添加到这些索引中的一个或多个。您绝对应该阅读multi-column indexinggrouping;良好的索引是您数据类型成功的关键因素。

您还应该在插入大量行后对每个表运行ANALYZE TABLE xxxx,以确保查询优化器可以看到有关表和索引内容的适当信息。

【讨论】:

  • 感谢您的回答奥利。我会阅读你的建议。关于您的建议,查询现在大约需要 10 秒来执行和 1.5 秒来获取。值得一提的是,有 4,000 行被返回。我创建了另一个 SQL Fiddle 来显示更改:sqlfiddle.com/#!9/e0db4
猜你喜欢
  • 2011-05-22
  • 2013-04-16
  • 2020-05-21
  • 1970-01-01
  • 2011-01-22
  • 2011-07-07
  • 2018-12-21
  • 2010-12-15
相关资源
最近更新 更多