有什么方法可以优化这个MySQL查询?

时间:2010-04-20 21:46:13

标签: sql mysql

我的表格如下:

`MyDB`.`Details` (
  `id` bigint(20) NOT NULL,
  `run_id` int(11) NOT NULL,
  `element_name` varchar(255) NOT NULL,
  `value` text,
  `line_order` int(11) default NULL,
  `column_order` int(11) default NULL
);

我在存储过程中有以下SELECT语句

SELECT
  RULE
  ,TITLE
  ,SUM(IF(t.PASSED='Y',1,0)) AS PASS
  ,SUM(IF(t.PASSED='N',1,0)) AS FAIL
FROM
(
SELECT
  a.line_order
  ,MAX(CASE WHEN a.element_name = 'PASSED' THEN a.`value` END) AS PASSED
  ,MAX(CASE WHEN a.element_name = 'RULE' THEN a.`value` END) AS RULE
  ,MAX(CASE WHEN a.element_name = 'TITLE' THEN a.`value` END) AS TITLE
FROM
  Details a
WHERE
  run_id = runId
GROUP BY line_order
) t
GROUP BY RULE, TITLE;

* runId是存储过程的输入参数。

此查询大约需要14秒才能运行。该表有214856行,我正在过滤的特定run_id有162204条记录。它不是超级大功率机器,但我觉得我可以更有效地做到这一点。我的主要目标是按规则和标题进行汇总,并显示通过和失败计数列。

表创建脚本:

CREATE TABLE  `MyDB`.`Details` (
  `id` bigint(20) NOT NULL,
  `run_id` int(11) NOT NULL,
  `element_name` varchar(255) NOT NULL,
  `value` text,
  `line_order` int(11) default NULL,
  `column_order` int(11) default NULL,
  KEY `report_id` (`run_id`),
  KEY `element_name` (`element_name`),
  CONSTRAINT `Details_ibfk_1` FOREIGN KEY (`run_id`) REFERENCES `RunHistory` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

解释:

select `t`.`RULE` AS `RULE`,`t`.`TITLE` AS `TITLE`,sum(if((`t`.`PASSED` = _utf8'Y'),1,0)) AS `PASS`,sum(if((`t`.`PASSED` = _utf8'N'),1,0)) AS `FAIL` from (select `TAA`.`Details`.`line_order` AS `line_order`,max((case when (`TAA`.`Details`.`element_name` = _utf8'PASSED') then `TAA`.`Details`.`value` end)) AS `PASSED`,max((case when (`TAA`.`Details`.`element_name` = _utf8'RULE') then `TAA`.`Details`.`value` end)) AS `RULE`,max((case when (`TAA`.`Details`.`element_name` = _utf8'TITLE') then `TAA`.`Details`.`value` end)) AS `TITLE` from `TAA`.`Details` where (`TAA`.`Details`.`run_id` = 66) group by `TAA`.`Details`.`line_order`) `t` group by `t`.`RULE`,`t`.`TITLE`
id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra
1, 'PRIMARY', '<derived2>', 'ALL', '', '', '', '', 3068, 'Using temporary; Using filesort'
2, 'DERIVED', 'Details', 'ref', 'report_id', 'report_id', '4', '', 107563, 'Using where; Using temporary; Using filesort'

2 个答案:

答案 0 :(得分:3)

哇。以下是一些提示:

  1. 这里的第一个问题是使用TEXT字段。使用TEXT或BLOB时,将在磁盘而不是内存上创建所有临时表。尝试使用varchar(N),尽可能保持N尽可能小。奇怪的是,MySQL允许在TEXT字段上使用GROUPing BY。
  2. 我会考虑在('run_id','line_order','element_name')上创建复合索引。只是尝试一下,这样的索引可能会显着影响系统其他部分的性能。

答案 1 :(得分:2)

如果您可以将数据从属性值方法中规范化并避免转换它们,那么最大的收获就是。你能这样做吗?

您还可以在详细信息中显示SHOW INDEXES吗?

编辑: 似乎新的索引建议对你有用。你能满足我的好奇心并检查两件事:

以下是对聚合转换为连接的查询的重写,您能检查它与原始连接的比较吗?

SELECT
  RULE
  ,TITLE
  ,SUM(IF(t.PASSED='Y',1,0)) AS PASS
  ,SUM(IF(t.PASSED='N',1,0)) AS FAIL
FROM
(
SELECT
  a.line_order,
  a.value AS TITLE,
  b.value AS RULE,
  c.value AS PASSED
FROM
  Details a INNER JOIN 
  Details b ON a.line_order = b.line_order AND a.run_id = runId AND a.element_name = 'PASSED' INNER JOIN 
  Details c ON b.line_order = c.line_order AND b.run_id = runId AND b.element_name = 'RULE' AND c.element_name = 'TITLE' 
) t
GROUP BY RULE, TITLE;

关于加入的一些注释

  • 我认为对于TITLE,至少会有一个RULE和PASSED值,如果没有将INNER JOINS转为LEFT JOINS
  • 此外,我已经将连接条件中的where条件移动了,这有时可以提示mysql使用索引通常不会,但我不确定,如果你有倾向,你可以尝试连接条件将它们从ON移动到WHERE

最后,如果你在('run_id','line_order','element_name','value')上添加索引,你还能检查你的查询速度会发生什么变化吗?我不确定它会改进什么(它会增加索引的大小,但会削减对各行的访问权限),所以查看数字会很有意思(检查计划是否真的使用它)。

最后,关于原始查询的另一个注释 - 可能至少在一个步骤中进行聚合。你想进一步调查吗?