用SUM() OVER()计算每日累计销售额最简洁,必须写ORDER BY sale_date,否则结果为全表总和;若日期重复,应追加order_id等唯一字段确保排序稳定,或先按日期聚合再开窗。

用 SUM() OVER() 计算每日累计销售额
直接在 SELECT 中用窗口函数是最简洁、最标准的做法,不需要自连接或子查询。关键在于正确设置 ORDER BY 和 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW(默认行为,可省略)。
常见错误是漏写 ORDER BY —— 没有它,SUM() OVER() 就变成对整张表求和,不是“累计”。另外,如果日期字段含时间戳且未去重,可能因排序不稳定导致同日多行结果错位。
- 确保
ORDER BY字段唯一或加二级排序(如ORDER BY sale_date, order_id) - 若原始数据存在多笔同日销售,先按日期聚合再开窗更稳妥(见下一条)
- 示例:
SELECT sale_date, daily_amount,<br> SUM(daily_amount) OVER (ORDER BY sale_date) AS cumsum_sales<br>FROM (SELECT sale_date, SUM(amount) AS daily_amount<br> FROM sales GROUP BY sale_date) t;
用 AVG() OVER() 或 SUM() OVER() 做 7 日滚动销售额
滚动聚合本质是限制窗口范围,用 ROWS BETWEEN 6 PRECEDING AND CURRENT ROW 表示“当前行 + 往前 6 行”,共 7 行 —— 注意这不是自然日,而是按 ORDER BY 排序后的逻辑行。
真正按自然日滚动(比如“截至今天,过去 7 天含今天”),必须用 RANGE BETWEEN INTERVAL '6 days' PRECEDING AND CURRENT ROW(PostgreSQL/Oracle 支持),但 MySQL 8.0+ 仅支持 RANGE 配合数字类型,不支持日期间隔;SQL Server 不支持 RANGE 日期偏移。所以跨数据库时,优先用 ROWS + 预聚合补全缺失日期。
- 若某天无销售,
ROWS窗口不会自动跳过空白日,会导致滚动天数不足 —— 建议先用日历表LEFT JOIN补零 - MySQL 用户别硬套
RANGE,老实用JOIN日历表 +ROWS窗口更可控 - 示例(PostgreSQL,自然日滚动):
SELECT sale_date,<br> SUM(amount) AS daily_sales,<br> SUM(SUM(amount)) OVER (<br> ORDER BY sale_date<br> RANGE BETWEEN INTERVAL '6 days' PRECEDING AND CURRENT ROW<br> ) AS rolling_7d_sum<br>FROM sales GROUP BY sale_date;
当数据缺失日期时,如何让累计/滚动结果连续对齐?
业务数据常有断档(比如周末无销售),但报表要求每天都有值。此时不能只靠原表开窗,必须引入完整日期序列(日历表),否则 OVER(ORDER BY date) 的“前 N 行”会跳过空日,导致累计值突变、滚动窗口天数缩水。
最可靠做法:构造最小到最大日期的序列,LEFT JOIN 销售表,用 COALESCE(amount, 0) 补零,再在此结果集上开窗。否则哪怕只缺一天,第 7 天的滚动和就会少掉那天的量。
- 别依赖
GENERATE_SERIES()(PostgreSQL)或seq4()(Snowflake)临时生成 —— 生产环境应建物理日历表,带节假日标记 - SQL Server 可用
master..spt_values或 CTE 递归生成,但大数据量下性能差,仍推荐预建表 - 补零后开窗,
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW才真对应 7 个自然日
为什么不用自连接或相关子查询做累计?
虽然 (SELECT SUM(s2.amount) FROM sales s2 WHERE s2.sale_date 能算累计,但时间复杂度是 <code>O(n²),万级数据就明显卡顿;而 SUM() OVER() 是单次扫描,O(n)。
更大的问题是语义脆弱:子查询里若没加 GROUP BY 或忽略重复日期,结果会重复累加;且无法复用窗口定义做多个指标(如同时要累计销量、累计订单数),每多一个就得写一遍子查询。
- 窗口函数支持多指标复用:
SUM(...) OVER w,COUNT(...) OVER w共享同一WINDOW w AS (ORDER BY ...) - 子查询在 MySQL 5.7 或旧版 PostgreSQL 中无法在
SELECT里引用外层GROUP BY别名,容易报错Unknown column - 现代 SQL 引擎(包括 ClickHouse、Doris)对窗口函数做了深度优化,自连接反而触发不了向量化执行

















