ROWS BETWEEN 按物理行位置界定窗口,严格取相邻行;RANGE BETWEEN 按排序值分组,重复值行全纳入。时间滚动均值需先补日期再用 ROWS BETWEEN 6 PRECEDING AND CURRENT ROW。

ROWS BETWEEN 是什么,它和 RANGE BETWEEN 有什么本质区别
ROWS BETWEEN 定义的是基于物理行位置的窗口边界,数的是“第几行”,不关心值是否重复或间隔多大。而 RANGE BETWEEN 是按排序键的值来划分范围,相同值的行会被归入同一组——哪怕它们在结果集中相隔很远。
这意味着:如果你的排序列有重复值(比如多个订单同属 order_date = '2024-01-01'),RANGE BETWEEN 1 PRECEDING AND CURRENT ROW 可能一次性拉进十几行;但 ROWS BETWEEN 1 PRECEDING AND CURRENT ROW 永远只取紧邻的上一行 + 当前行,严格可控。
常见错误现象:AVG(sales) OVER (ORDER BY order_date RANGE BETWEEN 1 PRECEDING AND CURRENT ROW) 在日期密集时结果跳变剧烈,不是你想要的“最近两天销量均值”,而是“和当前日期相同或前一天的所有订单均值”。
怎么写一个可靠的“过去7天销售额滚动平均”
关键点在于:时间维度不能直接用 ORDER BY date_col 配 ROWS,因为缺失日期会导致行数对不上。必须先补全日期,再用 ROWS BETWEEN 6 PRECEDING AND CURRENT ROW。
- 先用
GENERATE_SERIES(PostgreSQL)或递归 CTE(MySQL 8.0+/SQL Server)构造连续日期序列 - LEFT JOIN 原始销售表,把每天的
sales补为COALESCE(sales, 0) - 再套窗口函数:
AVG(sales) OVER (ORDER BY dt ROWS BETWEEN 6 PRECEDING AND CURRENT ROW)
示例(PostgreSQL):
SELECT dt,
sales,
ROUND(AVG(sales) OVER (ORDER BY dt ROWS BETWEEN 6 PRECEDING AND CURRENT ROW), 2) AS avg_7d
FROM (
SELECT d.dt, COALESCE(t.sales, 0) AS sales
FROM GENERATE_SERIES('2024-01-01'::DATE, '2024-01-31'::DATE, '1 day') AS d(dt)
LEFT JOIN sales_table t ON d.dt = t.sale_date
) AS filled;
ROWS BETWEEN 的边界参数哪些能省略,哪些绝对不能省
CURRENT ROW 可以简写为 CURRENT ROW 或直接省略(如 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 中的 CURRENT ROW 可不写),但起始和结束必须成对出现语义才完整。
容易踩的坑:
-
ROWS BETWEEN 2 PRECEDING—— 缺少AND ...,语法报错:ERROR: window frame with offset must specify both starting and ending bounds -
ROWS BETWEEN 1 FOLLOWING AND 1 PRECEDING—— 逻辑矛盾,PostgreSQL 报错,MySQL 可能静默返回空值 -
ROWS BETWEEN 5 PRECEDING AND 5 FOLLOWING在首尾几行会自动截断(即实际参与计算的行数
性能影响:ROWS 越宽,执行计划越容易崩
窗口帧宽度直接影响内存占用和排序开销。当 ROWS BETWEEN 10000 PRECEDING AND CURRENT ROW 遇到百万级结果集时,PostgreSQL 可能触发 work_mem 不足,降级为磁盘排序;SQL Server 则可能拒绝执行并报 Query processor could not produce a query plan。
优化建议:
- 避免无意义的大数字,比如
ROWS BETWEEN 1000000 PRECEDING AND CURRENT ROW实际只想表达“所有前面的行”,应改用UNBOUNDED PRECEDING - 在
ORDER BY字段上建索引,能让窗口计算复用排序结果,大幅降低 CPU 和内存压力 - 如果只是求累计值(如 running sum),且数据已按时间排序入库,考虑用应用层流式累加替代数据库窗口函数
真正难处理的从来不是语法,而是当 ROWS BETWEEN 遇上稀疏时间、高基数分组、超长跨度时,如何让结果既正确又跑得动——这时候得回过头检查数据分布和物化策略,而不是调大 work_mem。










