【问题标题】:Optimizing query with GROUP BY to remove Using Temporary; Using Filesort使用 GROUP BY 优化查询以删除 Using Temporary;使用文件排序
【发布时间】:2014-09-22 11:55:41
【问题描述】:

我正在使用 mySQL 5.6.13.2 并且有一个查询涉及父表中的 150,000 行,子表中的行超过 1M。如果我删除 GROUP BY(仅作为测试),查询需要 2 秒,如果我有 GROUP BY,则需要超过 6 秒,这是需要的。

我已阅读其他有关如何使用临时删除的帖子;使用文件排序,但这些并不能解决问题。我希望在这里得到一些帮助。

演示所有这些的 SQL 小提琴可在此处获得:http://sqlfiddle.com/#!9/edeb6/1

CREATE TABLE `summary` (
   `RunID` int(10) unsigned NOT NULL AUTO_INCREMENT,
   `LastUpdate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
   `FileName` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
   `XCount` int(11) DEFAULT NULL,
   `YCount` int(11) DEFAULT NULL,
   `AccountID` varchar(25) COLLATE utf8_unicode_ci DEFAULT NULL,
   PRIMARY KEY (`RunID`),
   KEY `acct-lastupdate` (`AccountID`,`LastUpdate`),
   KEY `acct-lastupdate-counts` (`AccountID`,`LastUpdate`,`XCount`,`YCount`)
   ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;



CREATE TABLE `detail` (
  `DetailID` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `LastUpdate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `RunID` int(10) unsigned DEFAULT NULL,
  `TestID` varchar(80) COLLATE utf8_unicode_ci DEFAULT NULL,
  `ResultCode` int(11) DEFAULT NULL,
   PRIMARY KEY (`DetailID`),
  KEY `detail_runid` (`RunID`),
  KEY `detail_testid` (`TestID`),
  KEY `detail_runid_testid_result` (`RunID`,`TestID`,`ResultCode`)
  ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

这是我的查询的 EXPLAIN 输出:

EXPLAIN select
      testid as 'TestID',
      sum(case when resultcode = 1 then 1 else 0 end) as Category1,
      sum(case when resultcode = 2 then 1 else 0 end) as Category2,
      sum(case when resultcode = 0 then 1 else 0 end) as Category3
      from detail d, summary s
      where s.accountid = 'xyz'
        and s.lastupdate >= '2014-05-26 00:00:00'
        and s.lastupdate < '2014-07-27 00:00:00'
        and s.runid = d.runid
        and s.runid <= 9999999999
      GROUP BY testid;

 1  SIMPLE  s   ref PRIMARY,acct-lastupdate,acct-lastupdate-counts  acct-lastupdate 78  const   2   Using where; Using index; Using temporary; Using filesort
 1  SIMPLE  d   ref detail_runid,detail_runid_testid_result detail_runid    5   db_9_edeb6.s.RunID  1   (null)

如果我删除 GROUP BY,那么 EXPLAIN 会显示 Using where;使用没有临时或文件排序的索引,查询运行时间为 2 秒而不是 6 秒。

必须将这些结果按测试 ID 分组。此外,测试 ID 值是任意的,并且事先不知道,因此无法针对硬编码的已知测试 ID 编写带有子查询的查询。

是否可以定义其他可能停止临时和文件排序的索引?如果没有,是否有更有创意的方式来重写这个查询,这样会更有效并可能解决这个问题?

请注意,在 GROUP BY 之后,我的查询确实有一些 HAVING 和 ORDER BY 条件(特别是它去...... GROUP BY testid 有 Category1 OR Category2 OR Category3 order by Category1 desc, Category2 desc;” - 但是我忽略了这个这里的例子是因为我得到了相同的性能和 EXPLAIN 输出有或没有扩展子句,我想保持样本尽可能简单。我在这里提到它是因为如果你有一个创造性的方法来重写查询,如果你能请包括它会很好。

如前所述,这里有一个 SQL fiddle http://sqlfiddle.com/#!9/edeb6/1 演示了该问题(因此您可以查看 EXPLAIN 输出和实验)。

谢谢!

【问题讨论】:

  • 必须是一条 SQL 语句吗?带有游标的存储过程是否可以选择?
  • 感谢您的评论草本。是的,它必须是单个 SQL 语句(当然可能包含子查询)。

标签: mysql sql query-optimization database-performance sqlperformance


【解决方案1】:

如果可以,请尝试将“accountid”字段添加到“detail”表中。然后您不需要为此查询加入汇总表。从查询中删除“summary”表并将“s”别名指向“d”。然后 EXPLAIN 仅显示使用 where。但我不知道它是否比你的快。

而语句“sum(case when resultcode = 1 then 1 else 0 end)”你可以写得更短,像这样“sum(resultcode=1) as Category1, sum(reusltcode=2) as Category2 ...”

【讨论】:

  • 感谢您的意见,johannes。出于几个原因,我认为将 AccountID 添加到详细信息表中并不是一个好主意。一是它们很长,并且将使用更多的数据存储(详细信息行比父摘要行多 10 倍)。此外,为简单起见,此处未显示,但摘要中还有其他数据列可用于过滤选择,并且还需要将这些数据列复制到表中。会变得乱七八糟。无论如何,只是为了咧嘴笑和咯咯笑,我试过了,但 EXPLAIN 仍然使用 temp;使用文件排序。见sqlfiddle.com/#!9/cd339/1
  • 这很奇怪。在我的示例中,我只看到“使用位置”:sqlfiddle.com/#!9/edeb6/7
  • 我认为您可能发布了错误的链接?你发的那个和我原来的一样。同样在不同的注释 - 感谢关于在 resultcode = 1 为 sum(resultcode=1) 等时重写案例的提示 - 它提供了相同的结果并将查询速度提高了 5%。嘿,每一点都有帮助。大的改进将来自摆脱临时和文件排序......关于如何做到这一点的任何想法?
  • 哦,对不起,你在这里是正确的链接:sqlfiddle.com/#!9/fcd39/1
  • 好的 - 不同之处在于我仍然有 RunID detail_runid_testid_result (AccountID,LastUpdate, TestID,ResultCode) 。这会导致使用 where;使用索引 - 但也使用临时和使用文件排序又回来了。所以我认为我们已经绕了一圈又回到了我原来的问题。如何构造查询以避免临时和文件排序?最好使用我最初演示的两表方法。谢谢!
猜你喜欢
  • 2017-09-09
  • 2011-09-23
  • 2023-04-09
  • 2011-06-14
  • 2020-08-23
  • 1970-01-01
  • 2014-02-13
  • 1970-01-01
  • 2011-08-29
相关资源
最近更新 更多