MySQL 计算移动平均线时的自连接优化
MySQL self-join optimization while calculating move averages
我创建了一个 mysql 查询来使用多个自联接来计算数据的移动平均值,如下所示。这会消耗大量时间,并且每个查询的数据行数为 100k。请问有什么方法可以进一步优化它以减少时间吗?
select a.rownum,a.ma_small_price, b.ma_medium_price from
(SELECT t3.rownum, AVG(t.last_price) as 'ma_small_price'
FROM temp_data t3
left JOIN temp_data t ON t.rownum BETWEEN ifnull(t3.rownum,0) - @psmall AND t3.rownum
GROUP BY t3.rownum)
inner join
(SELECT t3.rownum, AVG(t.last_price) as 'ma_medium_price'
FROM temp_data t3
left JOIN temp_data t ON t.rownum BETWEEN ifnull(t3.rownum,0) - @pmedium AND t3.rownum
GROUP BY t3.rownum) b on a.rownum = b.rownum
既然你是 运行 MySQL 8 岁,你应该能够使用 window 函数来更有效地获得相同的结果。没有看到示例数据,很难 100% 确定,但这应该很接近。请注意,要在 window 框架中使用变量,您需要使用准备语句:
SET @sql = '
SELECT rownum,
AVG(last_price) OVER (ORDER BY rownum ROWS BETWEEN ? PRECEDING AND CURRENT ROW) AS ma_small_price,
AVG(last_price) OVER (ORDER BY rownum ROWS BETWEEN ? PRECEDING AND CURRENT ROW) AS ma_medium_price
FROM temp_data';
PREPARE stmt FROM @sql;
EXECUTE stmt USING @psmall, @pmedium;
OVER ( ... )
慢得令人失望——在 MySQL 8.0 和 MariaDB 10.x.
中
我喜欢 "exponential moving average",因为它比 "moving average" 更容易计算。以下内容大致等同于 Nick 提出的内容。这运行得更快,但结果略有不同:
SELECT rownum,
@small := @small + 0.5 * (last_price - @small) AS mae_small_price,
@med := @med + 0.2 * (last_price - @med) AS mae_med_price
FROM ( SELECT @small := 10, @med := 10 ) AS init
JOIN temp_data
ORDER BY rownum;
系数控制指数移动平均线适应数据变化的速度。它应该大于 0 且小于 1。
我将 EPA 初始化为的“10”是对平均值的粗略猜测——它会偏向前几个值,但随着更多值的加入逐渐被淹没。
我创建了一个 mysql 查询来使用多个自联接来计算数据的移动平均值,如下所示。这会消耗大量时间,并且每个查询的数据行数为 100k。请问有什么方法可以进一步优化它以减少时间吗?
select a.rownum,a.ma_small_price, b.ma_medium_price from
(SELECT t3.rownum, AVG(t.last_price) as 'ma_small_price'
FROM temp_data t3
left JOIN temp_data t ON t.rownum BETWEEN ifnull(t3.rownum,0) - @psmall AND t3.rownum
GROUP BY t3.rownum)
inner join
(SELECT t3.rownum, AVG(t.last_price) as 'ma_medium_price'
FROM temp_data t3
left JOIN temp_data t ON t.rownum BETWEEN ifnull(t3.rownum,0) - @pmedium AND t3.rownum
GROUP BY t3.rownum) b on a.rownum = b.rownum
既然你是 运行 MySQL 8 岁,你应该能够使用 window 函数来更有效地获得相同的结果。没有看到示例数据,很难 100% 确定,但这应该很接近。请注意,要在 window 框架中使用变量,您需要使用准备语句:
SET @sql = '
SELECT rownum,
AVG(last_price) OVER (ORDER BY rownum ROWS BETWEEN ? PRECEDING AND CURRENT ROW) AS ma_small_price,
AVG(last_price) OVER (ORDER BY rownum ROWS BETWEEN ? PRECEDING AND CURRENT ROW) AS ma_medium_price
FROM temp_data';
PREPARE stmt FROM @sql;
EXECUTE stmt USING @psmall, @pmedium;
OVER ( ... )
慢得令人失望——在 MySQL 8.0 和 MariaDB 10.x.
我喜欢 "exponential moving average",因为它比 "moving average" 更容易计算。以下内容大致等同于 Nick 提出的内容。这运行得更快,但结果略有不同:
SELECT rownum,
@small := @small + 0.5 * (last_price - @small) AS mae_small_price,
@med := @med + 0.2 * (last_price - @med) AS mae_med_price
FROM ( SELECT @small := 10, @med := 10 ) AS init
JOIN temp_data
ORDER BY rownum;
系数控制指数移动平均线适应数据变化的速度。它应该大于 0 且小于 1。
我将 EPA 初始化为的“10”是对平均值的粗略猜测——它会偏向前几个值,但随着更多值的加入逐渐被淹没。