Oracle 19c中无法用AVG()窗口函数直接实现移动加权平均,必须用SUM() OVER ()分别计算加权和与权重和再相除,并注意时序方向、权重归一化、NULL处理及性能优化。

移动加权平均在Oracle 19c里不能直接用AVG()窗口函数实现
Oracle 19c 的 AVG() 窗口函数只支持简单算术平均,不接受权重参数。想算移动加权平均(比如最近3天销量按0.5/0.3/0.2加权),必须手动展开计算逻辑——核心是用 SUM() 窗口函数分别对「值×权重」和「权重」做滑动求和,再相除。
用SUM() OVER ()配合行偏移构造权重系数
关键在于把权重“绑定”到每行的相对位置上。假设按时间倒序,当前行权重0.5、前一行0.3、前两行0.2,可用 ROW_NUMBER() OVER (ORDER BY dt DESC) 生成序号,再用 CASE 映射权重:
SELECT dt, sales,
SUM(
CASE ROW_NUMBER() OVER (ORDER BY dt DESC) - ROW_NUMBER() OVER (ORDER BY dt DESC)
WHEN 0 THEN sales * 0.5
WHEN 1 THEN sales * 0.3
WHEN 2 THEN sales * 0.2
ELSE 0 END
) OVER (ORDER BY dt DESC ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING) /
SUM(
CASE ROW_NUMBER() OVER (ORDER BY dt DESC) - ROW_NUMBER() OVER (ORDER BY dt DESC)
WHEN 0 THEN 0.5
WHEN 1 THEN 0.3
WHEN 2 THEN 0.2
ELSE 0 END
) OVER (ORDER BY dt DESC ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING) AS wma_3
FROM sales_data;
注意:ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING 是为了匹配“当前+后两行”的时序逻辑(若按时间升序,则改用 2 PRECEDING);实际中更推荐用 ORDER BY dt ASC ROWS BETWEEN 2 PRECEDING AND CURRENT ROW 避免歧义。
权重归一化必须显式做,否则结果失真
常见错误是只算分子、漏掉分母的加权和,导致结果放大或缩小。尤其当窗口内有效行数不足(如开头两天)时,分母可能小于1,直接除会放大数值:
- 务必对权重项也做一次
SUM() OVER (),不能写死为常量和 - 如果权重随窗口内实际行数动态变化(如用行数倒序做权重),需用
COUNT(*) OVER ()辅助计算 - 遇到
NULL值要提前用NVL(sales, 0)处理,否则整段窗口计算中断
性能敏感场景建议物化中间权重列
每次查询都实时算 ROW_NUMBER() + CASE + 两次 SUM() OVER (),执行计划里会出现多轮窗口排序。若该指标被高频调用:
- 在表上加虚拟列:
ALTER TABLE sales_data ADD (wgt_factor AS (CASE ... END)) - 对虚拟列建函数索引(如果过滤条件固定)
- 更彻底的方案:用物化视图预计算带权重的
sales_wgt和wgt列,避免每次重复解析
真正难的不是写出来,而是确认权重分配是否符合业务语义——比如“最近”是指物理时间顺序,还是事件发生顺序,这个一旦错,整个加权就跑偏了。











