running total 是窗口函数中通过 sum() over() 实现的累计求和,即按 order by 指定顺序对当前行及之前所有行累加某列值;缺 order by 则退化为组内总和,非累计。

什么是窗口函数里的 RUNNING TOTAL?
它不是独立函数,而是用 SUM() 配合 OVER() 实现的累计求和行为:对当前行及之前所有行(按指定顺序)累加某列值。关键在于 ORDER BY 子句——没它,SUM() OVER() 就退化成全组总和,不是“累计”。
常见错误现象:SUM(sales) OVER (PARTITION BY region) 没写 ORDER BY,结果每行都显示该地区的总销售额,而非逐行递增。
- 必须显式声明排序逻辑,比如
ORDER BY order_date或ORDER BY id - 排序字段最好有唯一性保障,否则相同值的多行会得到相同累计值(SQL 标准行为)
-
PARTITION BY决定“分组边界”,ORDER BY决定“累计方向”
怎么写一个带分组的累计销售额?
假设表 orders 有字段 region、order_date、amount,要算每个地区内按日期递增的累计销售额:
SELECT
region,
order_date,
amount,
SUM(amount) OVER (
PARTITION BY region
ORDER BY order_date, id
) AS running_total
FROM orders;
说明:
-
PARTITION BY region确保各地区独立累计 -
ORDER BY order_date, id解决同日多单的排序歧义,避免并列累计值 - 若
order_date是DATETIME类型且精度足够,可省略id - 某些数据库(如 MySQL 8.0+、PostgreSQL、SQL Server)支持此语法;SQLite 3.25+ 也支持,但旧版不支持
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 是必须写的吗?
不是必须,但建议显式写出。默认窗口帧(frame)在多数引擎中确实是 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,但 PostgreSQL 在只有 ORDER BY 时默认用 RANGE 模式,可能引发意外重复累计。
问题场景:当 ORDER BY 字段存在重复值(如多个订单同一天),RANGE 会把同值所有行视为“同一帧”,导致累计值跳变。
- 安全写法:
SUM(amount) OVER (PARTITION BY region ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) -
ROWS按物理行序累计,RANGE按排序值逻辑累计 - 只要想严格逐行累加,就用
ROWS显式声明
性能和 NULL 值要注意什么?
累计计算本身不慢,但排序开销大;如果 ORDER BY 字段没索引,大数据量下延迟明显。
NULL 值影响更隐蔽:SUM() 会自动忽略 NULL,但若 ORDER BY 字段为 NULL,不同数据库处理不同——有的排最前,有的最后,可能导致累计起点错位。
- 确保排序字段非空,或用
COALESCE(order_date, '1970-01-01')统一兜底 - 累计列本身不会因某行
amount为 NULL 而中断,只是不计入该行 - 如需把 NULL 当 0 累加,写成
SUM(COALESCE(amount, 0)) OVER (...)
PARTITION BY、ORDER BY、帧定义三者稍有错配,结果就完全不对——尤其是跨数据库迁移时,RANGE 和 ROWS 的默认差异最容易被忽略。











