AVG()是SQL中唯一能直接实现移动平均的聚合函数,因其支持OVER()与ROWS BETWEEN且自动跳过NULL;SUM()/COUNT()等需手动组合,易出错且NULL处理不一致。

AVG() 是唯一能直接算移动平均的聚合函数
SQL里没有 MOVING_AVERAGE() 这种原生函数,AVG() 是唯一被标准支持、可配合 OVER() + ROWS BETWEEN 实现真正移动平均的聚合函数。其他如 SUM()、COUNT() 虽然也能用在窗口中,但必须手动组合才能模拟移动平均(比如 SUM(x) OVER(...) / COUNT(x) OVER(...)),既冗余又易错,还可能因 NULL 处理不一致导致结果偏差。
为什么 SUM() OVER() 不能直接替代 AVG() OVER()
有人试图用 SUM(amount) OVER(ORDER BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) 再除以 3 来“手算”移动平均,这存在几个硬伤:
- 窗口内实际行数可能不足 3(比如首两行),硬除 3 会低估均值
-
amount为NULL时,SUM()返回NULL,而AVG()默认跳过NULL并基于非空值计数——行为更符合业务直觉 - 数据类型隐式转换风险更高:例如
SUM(smallint)返回int,再除以常数可能触发浮点截断,而AVG()的返回类型有明确定义(见 SQL Server 文档中decimal(38, max(s,6))规则)
COUNT()、MIN()、MAX() 等函数无法单独实现移动平均
这些函数虽然支持 OVER(),但它们不提供均值所需的“求和+计数”联合语义:
-
COUNT()只能告诉你窗口里有多少非 NULL 值,无法提供分子 -
MIN()/MAX()是极值,不是中心趋势度量,和移动平均目标无关 - 强行拼接(如
SUM() / COUNT())需确保两个窗口定义完全一致,且要额外处理分母为 0 或全 NULL 的边界,实操中远不如单个AVG()稳定
注意 DISTINCT 在 AVG() OVER() 中基本无用
AVG(DISTINCT x) OVER(...) 在绝大多数数据库(PostgreSQL、SQL Server、BigQuery)中**不被允许**,会报错类似 ERROR: DISTINCT is not supported with window functions。即使某方言支持,它也违背移动平均本意——移动平均是对连续时序点的等权平滑,去重会破坏时间密度和物理意义。真要排重,应在窗口计算前用子查询或 CTE 清洗数据。
真正容易被忽略的是:AVG() 在窗口中对 NULL 的静默忽略,有时是陷阱。比如某天销售额为 NULL,你希望它参与计数(视为 0),就得写成 AVG(COALESCE(amount, 0)) OVER(...);否则结果会比预期偏高,且难以排查。

















