我必须在mysql数据集中计算移动平均值(不同周期)。我尝试了两种方法来计算平均值,但是两者都花费了大量时间。分享下面的代码。
方法:-1
select t1.*,
(select avg(t2.last_price)
from temp_data t2
where t2.rownum>t1.rownum-50 and t2.rownum<=t1.rownum and t1.script_code=t2.script_code) as 'ma_small_price'
from temp_data t1;
方法:-2
select t1.*, avg(t2.last_price) 'ma_small_price'
from temp_data t1
join temp_data t2
where t2.rownum>t1.rownum-50 and t2.rownum<=t1.rownum and t1.script_code=t2.script_code
group by t1.id,t1.date, t1.time;
这是表结构:
CREATE TABLE `temp_data` (
`id` int(11) NOT NULL DEFAULT '0',
`rownum` int(11) DEFAULT NULL,
`script_code` float DEFAULT NULL,
`date` date DEFAULT NULL,
`time` time DEFAULT NULL,
`last_price` float DEFAULT NULL,
`last_qty` float DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
rownum是具有连续行号的列。 ID是主键,但不是连续键,因此我必须添加一个单独的列
示例数据链接:https://www.dropbox.com/s/z8iacqvlkjdx6ax/temp_data_sample.xlsx?dl=0
接下来,我必须对同一数据平行计算多个移动平均值,但是周期(在上面的代码中指定为50)不同。
我的数据集非常庞大且正在增长(> 100万行),并且运行这些查询所花费的时间非常长-每个〜20分钟。寻求有关如何改进这些查询以减少运行时间的投入。谢谢!
答案 0 :(得分:1)
好问题 挑战在于对每行的迭代进行分组以进行跳转 因此,我们需要定义一个开始时间段和一个结束时间段,并在这些时间段之间连接同一张表
由于表格的大小,我添加了按和限制的订单
我还将索引添加到rownum列,以使连接和组运行更快
希望有帮助
ALTER TABLE temp_data ADD key rownum (rownum) ;
SELECT
t3.rownum AS endp,
AVG(t3.last_price)
FROM
temp_data t3
INNER JOIN temp_data t ON t.rownum BETWEEN MAX(IFNULL(t3.rownum, 0)) - 50 AND t3.endp
GROUP BY
endp
ORDER BY rownum DESC
LIMIT 0,1000
答案 1 :(得分:0)
好的。首先,只有1M行,这不需要20分钟。更像是20秒。如果您的rownum列是唯一的,则应将其索引为唯一键。它也应该是一个无符号的int。这样做会大大减少查询时间,因为现在看来您正在为每个联接进行完整的未排序表扫描。
第二,除非有某些原因对于比较大量历史数据的数据库不明显,否则应使用ISAM表,而不是InnoDB。
第三,必须对script_code建立索引,否则您将进行全表扫描。
更多: *方法2中的join语句是将每一行连接到每一行,然后执行where。您应该在rownum> t1.rownum-50和rownum <= t1.rownum上左联接,而不是执行常规联接然后运行where。即使没有索引rownum,这也将大大加快查询速度。 *如果希望获得更多数据,则还应考虑根据rownum对表进行分区。分区对于加速此类读取非常有用,因为您正在访问的大多数数据都是顺序的,并且将落在一个或两个分区内,因此可以加快读取速度。对于您的情况,您还可以按日期进行分区,这对于其他操作可能很方便。 *查看EXPLAIN SELECT并查看联接上正在使用的键。考虑使用USE INDEX提示以使用rownum代替联接的主键。
看来,您自己的查询似乎都不正确。完成上述优化后,我的猜测是您的方法1(子查询)将比没有方法2中的WHERE的正确JOIN ON更快。
那时候,您应该使用EXPLAIN SELECT来查看每个查询中正在执行的操作。它将显示正在读取和联接的行数以及正在使用的索引,从而帮助您缩小未索引联接的所有问题。