相关子查询可模拟累计求和,但需明确排序字段并建索引,否则性能差且结果不可靠;推荐优先使用窗口函数SUM() OVER(ORDER BY ...),MySQL 5.7等旧版本可用用户变量临时替代。

子查询实现累计求和的典型写法
SQL标准语法里没有内置的累计函数(如SUM() OVER()这种窗口函数),但用相关子查询可以模拟出来。核心思路是:对每一行,用子查询计算“从第一行到当前行”的某列总和。
常见错误是忽略排序依据——累计值必须基于明确的顺序,否则结果不可靠。比如按时间、ID或序号字段排序,否则子查询返回的“前N行”是随机的。
示例(以订单表orders为例,按order_date累计金额):
SELECT order_id, order_date, amount, (SELECT SUM(amount) FROM orders o2 WHERE o2.order_date <= o1.order_date) AS cum_amount FROM orders o1 ORDER BY order_date;
注意:o1和o2是表别名,用于区分外层和内层查询;<=保证包含当前行本身。
性能差在哪?为什么不能直接套用
相关子查询本质是“每行执行一次子查询”,数据量大时会严重拖慢。1万行数据可能触发1万次全表扫描(除非有合适索引)。
容易踩的坑:
- 没给排序字段建索引——
WHERE o2.order_date <= o1.order_date无法走索引范围扫描 - 用
!=或<代替<=,导致当前行被漏掉 - 多字段排序时没在子查询中严格复现排序逻辑(比如
order_date, order_id组合排序,子查询只比order_date)
MySQL 8.0+ 或 PostgreSQL 推荐改用窗口函数
如果数据库支持窗口函数(MySQL ≥ 8.0,PostgreSQL ≥ 8.4,SQL Server ≥ 2005),直接用SUM() OVER()更安全高效:
SELECT order_id, order_date, amount, SUM(amount) OVER (ORDER BY order_date, order_id) AS cum_amount FROM orders;
关键点:
-
ORDER BY必须明确,否则OVER()默认按无序处理,累计结果不稳定 - 多个排序字段要和业务逻辑一致,比如同一天多笔订单需用
order_id保序 - 不加
PARTITION BY就是全局累计;加了就分组累计(如按用户ID分组)
兼容老版本 MySQL(5.7 及更早)的替代方案
MySQL 5.7 不支持窗口函数,又不想用低效子查询?可以用变量模拟:
SET @cum := 0; SELECT order_id, order_date, amount, (@cum := @cum + amount) AS cum_amount FROM orders ORDER BY order_date, order_id;
但要注意:
- 必须先
SET初始化变量,且ORDER BY必须写在查询末尾(MySQL执行顺序敏感) - 不能在子查询或视图里用变量,MySQL 5.7 中变量行为未定义
- 并发查询下变量可能被干扰,仅限单线程脚本或临时分析使用
真正上线的系统里,这类变量写法风险高,不如升级数据库或预计算存到冗余字段。

















