使用GROUP BY优化查询以删除使用临时;使用Filesort

时间:2014-07-30 17:52:14

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

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

我已经阅读了有关如何使用临时删除的其他帖子;使用filesort但这些都没有解决问题。我希望能得到一些帮助。

可以在这里找到演示所有这些内容的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,则说明使用where;使用没有临时或文件排序的索引,查询在2秒而不是6秒内运行。

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

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

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

如上所述,这里有一个SQL小提琴http://sqlfiddle.com/#!9/edeb6/1来演示这个问题(所以你可以看到EXPLAIN输出和实验)。

谢谢!

1 个答案:

答案 0 :(得分:0)

如果是一个选项,则尝试将“accountid”字段添加到“detail”表中。然后,您不需要加入此查询的摘要表。从查询中删除“摘要”表,并将“s”别名指向“d”。然后EXPLAIN只显示使用where。但我不知道它是否比你的快得多。

语句“sum(结果代码= 1然后1其他0结束时的情况)”你可以像这样写“sum(resultcode = 1)作为Category1,sum(reusltcode = 2)作为Category2 ......”< / p>