在SQL的实际业务处理中,经常需要针对某个分组内的数据,按照时间、序号等顺序计算移动总和,比如统计每个用户近3天的消费总额、每个部门连续3个月的业绩累计等。这类需求可以通过窗口函数配合ROWS BETWEEN子句来高效实现,不需要复杂的自连接操作。

移动总和与ROWS BETWEEN基础概念
移动总和指的是在有序的数据集合中,针对当前行,取其前后指定范围内的行数据进行计算得到的总和。ROWS BETWEEN是窗口函数中用来定义窗口范围的子句,它明确指定了当前行前后参与计算的行的范围,是控制移动总和计算逻辑的核心部分。
ROWS BETWEEN的语法结构如下:
ROWS BETWEEN 起始边界 AND 结束边界
常见的边界取值包括:
- UNBOUNDED PRECEDING:窗口起始于分组的第1行
- N PRECEDING:窗口起始于当前行的前N行
- CURRENT ROW:窗口包含当前行
- N FOLLOWING:窗口结束于当前行的后N行
- UNBOUNDED FOLLOWING:窗口结束于分组的最后1行
分组内移动总和的实现步骤
1. 准备测试数据
我们首先创建一张用户消费记录表,包含用户ID、消费日期和消费金额三个字段,用于演示分组内的移动总和计算:
-- 创建测试表
CREATE TABLE user_consume (
user_id INT,
consume_date DATE,
amount DECIMAL(10,2)
);
-- 插入测试数据
INSERT INTO user_consume VALUES
(1, '2024-01-01', 100.00),
(1, '2024-01-02', 150.00),
(1, '2024-01-03', 200.00),
(1, '2024-01-04', 120.00),
(2, '2024-01-01', 80.00),
(2, '2024-01-02', 90.00),
(2, '2024-01-03', 110.00);
2. 计算分组内从起始到当前行的累计总和
如果需要计算每个用户从第一次消费到当前日期的累计消费总额,窗口范围可以定义为从分组第一行到当前行:
SELECT
user_id,
consume_date,
amount,
SUM(amount) OVER (
PARTITION BY user_id
ORDER BY consume_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS total_amount
FROM user_consume
ORDER BY user_id, consume_date;
上述查询中,PARTITION BY user_id表示按用户ID分组,ORDER BY consume_date保证组内按消费日期排序,ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW定义了窗口为组内从第一行到当前行的所有数据,最终SUM函数计算这个窗口内的金额总和。
3. 计算分组内近3行的移动总和
如果需要计算每个用户近3次消费的移动总和(包含当前行和前2行),窗口范围可以定义为前2行到当前行:
SELECT
user_id,
consume_date,
amount,
SUM(amount) OVER (
PARTITION BY user_id
ORDER BY consume_date
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS moving_3_total
FROM user_consume
ORDER BY user_id, consume_date;
此时对于用户1的2024-01-01记录,前2行不存在,窗口只有当前行,总和为100;2024-01-02记录,窗口包含前1行和当前行,总和为250;2024-01-03及之后的记录,窗口都包含当前行和前2行,总和为对应三行的金额之和。
不同窗口范围的计算结果对比
我们可以通过一个表格对比不同ROWS BETWEEN配置下的移动总和计算逻辑:
| ROWS BETWEEN配置 | 窗口范围说明 | 适用场景 |
|---|---|---|
| UNBOUNDED PRECEDING AND CURRENT ROW | 分组第一行到当前行 | 累计总和统计 |
| N PRECEDING AND CURRENT ROW | 当前行前N行到当前行 | 近N+1条记录的移动总和 |
| CURRENT ROW AND N FOLLOWING | 当前行到当前行后N行 | 未来N+1条记录的预累计 |
| N PRECEDING AND M FOLLOWING | 当前行前N行到当前行后M行 | 当前行前后指定范围的移动总和 |
注意事项
使用ROWS BETWEEN时需要注意几个问题:
- 如果分组内的行数不足窗口定义的行数,窗口会自动适配存在的行,不会报错,比如前2行不存在时只计算存在的行的总和
- ORDER BY子句在窗口函数中非常重要,没有排序的话行的顺序是不确定的,移动总和的结果也会不符合预期
- ROWS是基于行的物理位置计算范围,如果需要按值的范围计算(比如近3天的记录,不管中间有没有缺失日期),需要使用RANGE BETWEEN而不是ROWS BETWEEN
如果需要计算按值范围的移动总和,比如每个用户近3天(按日期差计算)的消费总和,可以参考以下示例:
SELECT
user_id,
consume_date,
amount,
SUM(amount) OVER (
PARTITION BY user_id
ORDER BY consume_date
RANGE BETWEEN INTERVAL 2 DAY PRECEDING AND CURRENT ROW
) AS range_3day_total
FROM user_consume
ORDER BY user_id, consume_date;
这个查询会计算同一个用户中,消费日期在当前日期前2天到当前日期之间的所有消费金额总和,和ROWS按行数计算的逻辑有明显区别。
SQL移动总和ROWS_BETWEEN窗口函数分组计算修改时间:2026-07-03 02:33:35