不能。所有主流数据库禁止在update的set子句中直接使用sum() over()等窗口函数,因其执行模型冲突;必须用子查询或cte预计算累计值,再通过join关联更新,并确保order by字段组合唯一且有索引。

不能直接用 UPDATE + 窗口函数做跨表累计更新;必须借助子查询、临时计算或 JOIN 搭配行序逻辑来实现。
为什么 UPDATE 不能直接套用 SUM() OVER()
多数数据库(如 MySQL 8.0+、PostgreSQL、SQL Server)允许在 SELECT 中用 SUM() OVER(),但 UPDATE 语句本身不支持窗口函数作为 SET 目标——会报类似 Window function is not allowed in this context 的错误。
- MySQL 报错:
ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'(如果误用子查询带 LIMIT 做关联) - PostgreSQL 报错:
ERROR: window functions are not allowed in UPDATE - 核心限制:UPDATE 的 SET 子句只接受标量表达式、子查询(返回单值)、或来自 FROM 的关联字段
用 JOIN + 子查询实现累计值写入(通用方案)
本质是把累计逻辑封装进一个派生表(子查询),再和目标表按主键或顺序字段 JOIN,然后 UPDATE。关键在于子查询必须能稳定排序并生成逐行累计值。
假设源表为 sales(含 id, amount, order_date),目标表为 sales_summary(含 id, cumulative_amount),需按 order_date 排序累计:
UPDATE sales_summary t
JOIN (
SELECT
id,
SUM(amount) OVER (ORDER BY order_date, id) AS cum_amt
FROM sales
) s ON t.id = s.id
SET t.cumulative_amount = s.cum_amt;
- 必须确保
ORDER BY中的字段组合能唯一确定行序(比如加id防止日期相同时序不确定) - MySQL 8.0+ 和 PostgreSQL 支持该写法;SQLite 不支持窗口函数,需改用自连接或递归 CTE
- 若目标表无对应
id,需先保证两表存在可关联的业务主键(如订单号)
MySQL 5.7 或不支持窗口函数的老版本怎么办?
只能用变量模拟累计,但要注意变量执行顺序不可靠,必须严格控制 ORDER BY 和 JOIN 顺序,且仅限单线程执行环境(如脚本中逐条跑)。
UPDATE sales_summary t
JOIN (
SELECT
id,
@cum := @cum + amount AS cum_amt
FROM sales
CROSS JOIN (SELECT @cum := 0) AS _
ORDER BY order_date, id
) s ON t.id = s.id
SET t.cumulative_amount = s.cum_amt;
- 变量初始化
@cum := 0必须放在FROM子句中,且ORDER BY不能丢——否则累计顺序错乱 - 该写法在 MySQL 8.0+ 中已被标记为“不推荐”,并发更新时可能出错
- 更稳妥的做法是先导出累计结果到临时表,再 UPDATE:先
CREATE TEMPORARY TABLE tmp_cum AS ...,再关联更新
容易被忽略的边界情况
累计更新不是纯数学运算,业务含义决定细节处理方式:
- 空值(
NULL):SUM()自动忽略,但如果某行amount是NULL,它不会中断累计,但也不会贡献值——确认是否要转成 0 再累加(用COALESCE(amount, 0)) - 重复时间戳:若
order_date有大量重复,仅靠它排序会导致累计值“跳变”(相同日期的多行共享同一累计值),必须引入第二排序字段(如自增id或created_at微秒级时间) - 目标表有额外过滤条件:比如只更新状态为
'active'的记录,要在 JOIN 后加WHERE t.status = 'active',而不是在子查询里过滤源表——否则关联会漏行
累计逻辑一旦涉及业务排序规则(比如“按用户下单时间,同用户内按金额降序”),就必须把完整排序表达式写进窗口函数或变量子查询的 ORDER BY,少一个字段就可能让整批数据累计错位。











