不使用 BETWEEN 的 SQL 滚动平均值

Rolling Average in SQL without using BETWEEN

假设我有一个 table signups 格式:

| date       | sign_ups |
|------------|----------|
| 2018-01-01 | 34       |
| 2018-01-02 | 23       |
| 2018-01-03 | 2        |
| ...        | ...      |

我现在想编写一个查询来计算前 7 天的平均注册量,即对于给定的一天,它的平均注册量和前六天的注册量值。 blog post 提供了这个解决方案:

select
  date,
  avg(sign_ups) 
    over (order by date asc
          rows between 6 preceding and current row) as avg,
from signups

我不喜欢使用 SQL 的 BETWEEN,所以我写了这个解决方案:

SELECT 
  a.date, 
  AVG(b.sign_ups)
FROM 
  signups a 
JOIN 
  signups b ON a.date <= b.date + interval '7 days'
GROUP BY 
  a.date

只是想确认两者是等价的,如果有更多concise/more有效的解决方案可以解决这个问题。

首先,假设你的意思是:

on b.date <= a.date and
   b.date > interval '7 days'

只有当每个日期只有一行时,它们才等价。

您的版本完全不同——您在 join 函数中的日期 之后 但在 windows 函数中的日期之前取值。在一种情况下您有 8 个值,在另一种情况下有一些未知数。但是我明白了问题的要点。

window 函数版本比自连接更受欢迎。从性能和可理解性的角度来看,这简直更好。而且,正如您的代码充分展示的那样,windows 版本可以更轻松地表达您的实际意图。

两个查询不等价:

1) 正如@GordonLinoff 所回答的那样,第一个查询 returns 与 table 中的记录一样多,而第二个查询按日期聚合。为了使结果相同,每个日期应该只有一行

2) 这个:

rows between 6 preceding and current row

不等同于:

a.date <= b.date + interval '7 days'

首先,正如 Dnoeth 所评论的,第二种形式将计算当前日期和过去 7 天的平均值,这代表总共 8 天

此外,第二个表格将考虑最近 N 天内的记录,以及当前日期之后的每条记录

您需要更改该子句以向日期范围添加上限,例如:

a.date <= b.date + interval '6 days' AND a.date >= b.date

这基本上模仿了 BETWEEN... 也可以拼写为 :

b.date BETEWEEN a.date - interval '6 days' AND a.date