我有以下查询:
SELECT location, step, COUNT(*), AVG(foo), YEAR(start), MONTH(start), DAY(start)
FROM table WHERE jobid = 'xxx' AND start BETWEEEN '2010-01-01' AND '2010-01-08'
GROUP BY location, step, YEAR(start), MONTH(start), DAY(start)
最初我在各个列上都有索引,例如 jobid 和 start ,但很快就意识到MySQL只是真正尊重select中每个表的一个索引。因此,它将使用 jobid 索引,然后通过 start 范围进行相当大的扫描过滤。
在( jobid , start )上添加索引有很大帮助,但GROUP BY仍然会导致性能问题。我已阅读docs on GROUP BY optimizations并了解为了从这些优化中受益,我需要一个包含( location , step , start <的索引/ em>),但我还有两个未解决的问题:
优化后的小组是否会使用时间函数(YEAR,MONTH,DAY等)?或者我是否必须将这些值存储为单独的列?我喜欢这些功能的原因是它意味着我可以在每个连接的基础上控制时区,并获得为最终用户时区定制的结果。如果我必须预先存储年,月和日,我将通过UTC进行,然后我的所有用户都将获得UTC报告。
即使我能解决问题#1,我甚至可以这样做吗?索引( jobid , start )帮助了WHERE子句,但GROUP BY需要一个不同的索引进行优化( location ,步骤,开始)或者,根据#1的答案,(位置,步骤,年< / em>,月,天)。但问题是这两个索引不共享一个常见的左侧列,所以我不相信我的WHERE和GROUP by可以兼容,以便使用相同的索引。所以我的问题是:我刚才在这里?
关于如何实现这一目标的任何其他想法都会有所帮助。并且,只是为了抢占可能出现的一些问题/评论:
有了上述说法,如果有人对如何做汇总或循环数据库这样的事情有任何设计建议,并且仍然得到时区特定的结果,我全都听见了!
更新:根据要求,这里有更多信息:
显示输出中的索引:
step 0 PRIMARY 1 step_id A 16 NULL NULL BTREE step 1 start 1 start A 16 NULL NULL BTREE step 1 step 1 step A 2 NULL NULL BTREE step 1 foo 1 foo A 16 NULL NULL YES BTREE step 1 location 1 location A 2 NULL NULL YES BTREE step 1 jobid 1 jobid A 2 NULL NULL YES BTREE
show create table output:
CREATE TABLE `step` ( `start` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', `step` smallint(2) unsigned NOT NULL, `step_id` int(8) unsigned NOT NULL AUTO_INCREMENT, `location` varchar(12) DEFAULT NULL, `jobid` varchar(37) DEFAULT NULL, PRIMARY KEY (`step_id`), KEY `start_time` (`start`), KEY `step` (`step`), KEY `location` (`location`), KEY `job_id` (`jobid`) ) ENGINE=InnoDB AUTO_INCREMENT=240 DEFAULT CHARSET=utf8
答案 0 :(得分:1)
相反,这样做
GROUP BY location, step, YEAR(start), MONTH(start), DAY(start)
ORDER BY location, step, YEAR(start), MONTH(start), DAY(start)
试
GROUP BY location, step, date_format(start, '%Y%m%d')
ORDER BY location, step, date_format(start, '%Y%m%d')
答案 1 :(得分:0)
并了解为了从这些优化中受益,我需要一个包含(位置,步骤,开始)的索引
不。如果没有jobid + start + location + step
,您可以创建复合索引BETWEEN
, 帮助。由于您在WHERE
中使用范围条件 - GROUP BY
不会使用任何索引,您可以为此查询执行的唯一且最好的事情就是jobid + start
索引。
最好的解决方案是将此表分解为某种预先计算的形式。例如:每小时按调度程序聚合数据。
答案 2 :(得分:0)
在jobid, start, location, step
然后先按该顺序分组,然后对其进行排序:
SELECT location, step, COUNT(*), AVG(foo), YEAR(start), MONTH(start), DAY(start)
FROM table WHERE jobid = 'xxx' AND start BETWEEEN '2010-01-01' AND '2010-01-08'
GROUP BY YEAR(start), MONTH(start), DAY(start), location, step
ORDER BY location, step, YEAR(start), MONTH(start), DAY(start)
更新
当使用YEAR,MONTH和DAY函数时,看起来MySql无法使用索引。因为
using filesort
y = YEAR(start), m = MONTH(start), d=DAY(start)
,在jobid, y, m, d, location, step
上创建索引并更新WHERE ... AND y = 2010 AND m = 12 AND d BETWEEN 1 AND 08
会删除using temporary using filesort
。保留3个额外的列似乎是一个坏主意,因为如果它使用临时或不使用GROUP BY之间的性能差异那么重要。
答案 3 :(得分:0)
如果位置和步长为整数外,则可能选择更快 密钥到其他表只有名称&amp;整数id。
首先,查询将被分组在整数数据上,这将比较快得多。 其次,数据库引擎可能会自动索引这些数字。
如果值重复,我还会考虑将jobid卸载到一个单独的表中。