Postgres 移动平均线

Posted

技术标签:

【中文标题】Postgres 移动平均线【英文标题】:Postgres moving average 【发布时间】:2021-01-18 05:26:31 【问题描述】:

正在尝试验证我的移动平均计算...但是我得到了两个不同的值,我希望每个查询都返回相同的值:

select sum(a.processing) / 50 as myMean
from (select created, processing
      from myTable
      where name = 'stack'
      order by created desc
      limit 50) a
union
select b.*
from (select AVG(processing) 
      filter (where name = 'stack') 
      OVER (ORDER BY created desc ROWS BETWEEN 49 PRECEDING AND CURRENT ROW)
      from myTable
      where name = 'stack'
      order by created desc
      limit 1) b

第一个查询我得到处理值的总和,然后除以 N(这里是 50),第二个查询我尝试使用窗口函数来实现同样的事情 - 获得最后 50 个的平均处理值name = stack 的行。

【问题讨论】:

有什么问题?这似乎完全符合你的描述?使用一些示例数据并期望输出会更容易理解您想要什么。 @T.Peter 我得到两个不同的值,我希望这些值是相同的 【参考方案1】:

ROWS BETWEEN 50 PRECEDING AND CURRENT ROW 子句将处理 51 行(前 50 行加上当前行)。

没有窗口函数的第一个查询将处理 50 行。

另外,你的OVER 子句应该是FOLLOWING,而不是PRECEDING

OVER (ORDER BY created desc ROWS BETWEEN CURRENT ROW AND 49 FOLLOWING ROW)

现在的写法,它只显示第一行的平均值,即第一行的值,而不是前50行的平均值。

【讨论】:

改成四十九了,每次查询还是得到不同的结果,会更新问题 @NimChimpsky,OVER 子句应该有FOLLOWING,而不是PRECEDING 框架。问题中的样本数据和预期结果通常有助于获得良好的正确答案。 @NimChimpsky,你可以玩简单的例子here。尝试不同的变体,你会看到它是如何工作的。这就是为什么拥有一些示例数据很有用的原因 - 您可以快速检查查询是否产生了预期的结果。【参考方案2】:

我认为OP误解的部分是Windows函数over中的order by

select b.*
from (select AVG(processing) 
      filter (where name = 'stack') --filter do nothing here 
      OVER (ORDER BY created desc ROWS BETWEEN 50 PRECEDING AND CURRENT ROW)
      from myTable
      where name = 'stack' --where clause already filter the name
      order by created desc
      limit 1) b 

让我们稍微修改一下以显示order by 在此查询中实际执行的操作。

select b.*
from (select AVG(processing) 
      OVER (ORDER BY created desc ROWS BETWEEN 50 PRECEDING AND CURRENT ROW)
      from myTable
      where name = 'stack'
      order by created desc
      ) b --remove limit

带有伪example

伪表:

| processing | created | name |
|------------|---------|------|
|          6 |       6 | a    |
|          5 |       5 | a    |
|          4 |       4 | a    |
|          3 |       3 | a    |
|          2 |       2 | a    |
|          1 |       1 | a    |

输出:

| avg | path      |
|-----|-----------|
|   6 |6          |
| 5.5 |6+5        |
|   5 |6+5+4      |
| 4.5 |6+5+4+3    |
|   4 |6+5+4+3+2  |
| 3.5 |6+5+4+3+2+1|

如您所见,没有limit 订单将尝试平均当前行和前面的行(在本例中为前面的 50 行)。 但是当您在查询中添加limit 1 时,它只会从输出中返回第一行,因此 OP 只会将第一行视为输出,这会使 OP 对不同的结果感到困惑。

TL;DR :

查询的第二部分基本上返回第一行结果(排序后),这是没有意义的,因为一个条目的平均值是……没有意义的。

这里是MSDN 以便更好地检查。


顺便说一句,Sum(col) / n 是获取平均值的危险方法,如果将列数据存储为整数,您将得到一个非常不准确的结果,例如,如果真正的平均值是 3.5,您将得到 3。如果平均是你的目标,你应该远离使用正确的函数avg()

【讨论】:

这是不正确的,当我指定 postgres 时,您还链接到 msdn 链接到 msdn 是一个糟糕的问题,但 postgres 聚合函数与 windows 函数是相同的。它们基本上是一样的。 你是什么意思不正确,你的查询做了我回答的事情,你使用over完全错误,我试图指出这一点。 极限并不像你想象的那样 限制按照我的想法做,唯一要做的就是指定要处理的行数...limit 1 = 第一行,limit 2= 第一行和第二行...

以上是关于Postgres 移动平均线的主要内容,如果未能解决你的问题,请参考以下文章

使用 Django ORM 进行快速移动平均计算

在 Postgres 中,如何平均每个用户的最新 5 个分数?

如何在 Postgres 中获取时间间隔的平均值

空闲的 postgres 进程占用大量内存

如何将数据从 Postgres 移动到在 Amazon 的 RDS 上运行的 MySQL?

Postgres 性能改进和清单