avg()配合rows between是最直接可靠的移动平均写法,因avg(col)仅返回全表静态均值,而移动平均需基于当前行位置滑动计算局部均值,必须用窗口函数;如“前1行+当前行+后1行”须写avg(col) over (order by ts rows between 1 preceding and 1 following)。

用窗口函数 AVG() 配合 ROWS BETWEEN 是最直接、最可靠的方式,其他方法(如自连接、子查询)在数据量大或存在重复时间戳时容易出错或性能崩塌。
为什么不能用 AVG() 直接套整个列?
直接写 SELECT AVG(col) FROM table 得到的是全表平均值,不是“当前行前后几行”的局部均值。你需要的是基于当前行位置滑动计算的动态平均——这属于典型的窗口计算场景,必须用窗口函数。
常见错误是试图用聚合 + GROUP BY 模拟,但那样会丢失原始行粒度,无法实现“每行都带一个对应窗口的均值”。
-
AVG(col) OVER ()→ 全表平均,无意义 -
AVG(col) GROUP BY id→ 分组后坍缩行数,无法保留原行 - 正确起点永远是
AVG(col) OVER (ORDER BY ... ROWS BETWEEN ...)
怎么写“前1行 + 当前行 + 后1行”的平均值?
核心是明确排序依据(通常是时间戳或自增 ID),再用 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING 定义窗口范围:
SELECT
ts,
value,
AVG(value) OVER (
ORDER BY ts
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
) AS avg_3rows
FROM sensor_data;
注意点:
- 必须有
ORDER BY,否则ROWS BETWEEN无定义(PostgreSQL 会报错,MySQL 8.0+ 和 SQL Server 也要求) - 如果
ts有重复值,相同ts的行会被归入同一“逻辑位置”,导致窗口包含意外多行;此时应加二级排序,如ORDER BY ts, id - 首尾行会自动截断:第1行只有“自身+后1行”,共2行参与计算,不是补零或跳过
想算“前2行 + 当前行”或“不包含当前行”怎么办?
窗口偏移量可自由组合,关键记清 PRECEDING(往前)、FOLLOWING(往后)、CURRENT ROW(当前,默认隐含):
- 前2行 + 当前行:
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW - 仅前1行和后1行(不含当前):
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW(PostgreSQL / SQL Server 支持;MySQL 8.0+ 不支持EXCLUDE,需用SUM(value) - value手动减) - 固定3行但按值范围(非行数):
RANGE BETWEEN INTERVAL '1 hour' PRECEDING AND CURRENT ROW(仅部分数据库支持,且对重复值敏感)
性能提示:行数窗口(ROWS)比值范围窗口(RANGE)快得多,尤其在大数据集上,优先选 ROWS。
遇到 window function is not allowed in WHERE/HAVING 怎么办?
窗口函数不能出现在 WHERE 或 HAVING 中,这是语法硬限制。想过滤均值结果,必须用子查询或 CTE:
WITH with_avg AS (
SELECT *,
AVG(value) OVER (ORDER BY ts ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS avg_3
FROM sensor_data
)
SELECT * FROM with_avg WHERE avg_3 > 25.5;
别试图在 WHERE 里直接写 AVG(...) > 25.5 —— 所有主流 SQL 引擎都会立刻报错,且这个错误不提示“该用 CTE”,只甩一句语法错误,容易卡住。
边界情况容易被忽略:当排序字段有大量 NULL,不同数据库处理方式不同(PostgreSQL 默认把 NULL 排最前,MySQL 可能排最后),会导致窗口计算顺序异常。上线前务必用真实 NULL 数据验证排序行为。











