【问题标题】:How do I calculate a moving average using MySQL?如何使用 MySQL 计算移动平均线?
【发布时间】:2010-10-27 01:57:00
【问题描述】:

我需要做类似的事情:

SELECT value_column1 
FROM table1 
WHERE datetime_column1 >= '2009-01-01 00:00:00' 
ORDER BY datetime_column1;

除了value_column1,我还需要检索value_column1的前20个值中的一个moving average

首选标准 SQL,但如有必要,我将使用 MySQL 扩展。

【问题讨论】:

    标签: sql mysql statistics


    【解决方案1】:

    在mysql 8中window function frame可以用来获取平均值。

    SELECT value_column1, AVG(value_column1) OVER (ORDER BY datetime_column1 ROWS 19 PRECEDING) as ma
    FROM table1
    WHERE datetime_column1 >= '2009-01-01 00:00:00' 
    ORDER BY datetime_column1;
    

    这会计算当前行和前 19 行的平均值。

    【讨论】:

      【解决方案2】:

      我意识到这个答案已经晚了大约 7 年。我有类似的要求,并认为我会分享我的解决方案,以防它对其他人有用。

      有一些用于技术分析的 MySQL 扩展,包括简单的移动平均线。它们非常易于安装和使用:https://github.com/mysqludf/lib_mysqludf_ta#readme

      安装 UDF 后(根据 README 中的说明),您可以在 select 语句中包含一个简单的移动平均线,如下所示:

      SELECT TA_SMA(value_column1, 20) AS sma_20 FROM table1 ORDER BY datetime_column1 
      

      【讨论】:

        【解决方案3】:

        我的解决方案在表格中添加了一个行号。以下示例代码可能会有所帮助:

        set @MA_period=5;
        select id1,tmp1.date_time,tmp1.c,avg(tmp2.c) from 
        (select @b:=@b+1 as id1,date_time,c from websource.EURUSD,(select @b:=0) bb order by date_time asc) tmp1,
        (select @a:=@a+1 as id2,date_time,c from websource.EURUSD,(select @a:=0) aa order by date_time asc) tmp2
        where id1>@MA_period and id1>=id2 and id2>(id1-@MA_period)
        group by id1
        order by id1 asc,id2 asc
        

        【讨论】:

        • 如果您使用条件从表中选择特定记录(此处命名为 websource.EURUSD),您必须在两个子选择(tmp1 和 tmp2 别名)中使用完全相同的条件
        【解决方案4】:

        根据我的经验,从 5.5.x 开始,Mysql 倾向于不使用依赖选择的索引,无论是子查询还是连接。这会对性能产生非常显着的影响,因为相关选择标准在每一行上都发生了变化。

        移动平均线是属于此类别的查询示例。执行时间可能会随着行的平方而增加。为了避免这种情况,选择了一个可以对依赖选择执行索引查找的数据库引擎。我发现 postgres 可以有效解决这个问题。

        【讨论】:

          【解决方案5】:

          Tom H 的方法会奏效。如果你有一个标识列,你可以像这样简化它:

          SELECT T1.id, T1.value_column1, avg(T2.value_column1)
          FROM table1 T1
          INNER JOIN table1 T2 ON T2.Id BETWEEN T1.Id-19 AND T1.Id
          

          【讨论】:

          • 我不了解 MySQL,但在 MS SQL Server 中将无法使用。 IDENTITY 列不保证是连续的或连续的。
          • 如果您不使用 SET IDENTITY_INSERT ON 或删除价格,它们会是什么?在这种情况下,您可以将数据移动到具有按日期排序的标识列的临时表中。
          • 我同意汤姆的观点。 IDENTITY(或 MySQL 用语,auto_increment 主键)可能不是连续的或连续的。如果从表格中间删除一些行怎么办?您的键中会有间隙。
          • 我同意这并不理想......但我赞成它,因为在某些地方,由于缺少记录等,这种方法的性能优势超过了可能的不准确性......
          【解决方案6】:

          当我遇到类似问题时,出于各种原因我最终使用了临时表,但这让这变得容易多了!就架构而言,我所做的看起来与您正在做的非常相似。

          使架构类似于 ID 标识、开始日期、结束日期、值。选择时,根据身份 ID 对前 20 个进行子选择平均。

          仅当您发现自己因为其他原因已经在使用临时表时才这样做(我一遍又一遍地针对不同的指标点击相同的行,因此拥有小型数据集很有帮助)。

          【讨论】:

          • 我不确定临时表的来源,没有它们我可以使用您的解决方案。虽然,它存在依赖连续标识列的问题。
          • 连续的标识列是临时表的重点.....就我而言,我有多年的数据,但每个月的数据都是在其上处理的自己的。我将数据提取到临时表并对其执行许多指标。就我而言,使用临时表(或表值函数)使处理的许多方面变得更容易。
          【解决方案7】:

          这只是我的想法,而且我正在出门的路上,所以它未经测试。我也无法想象它会在任何类型的大型数据集上表现得非常好。我确实确认它至少运行没有错误。 :)

          SELECT
               value_column1,
               (
               SELECT
                    AVG(value_column1) AS moving_average
               FROM
                    Table1 T2
               WHERE
                    (
                         SELECT
                              COUNT(*)
                         FROM
                              Table1 T3
                         WHERE
                              date_column1 BETWEEN T2.date_column1 AND T1.date_column1
                    ) BETWEEN 1 AND 20
               )
          FROM
               Table1 T1
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-11-18
            相关资源
            最近更新 更多