窗口函数比子查询更合理,直接用sum()窗口函数实现累计求和是现代sql标准做法;子查询性能差、可读性低、易出错,仅在数据库不支持窗口函数时不得已使用。

用窗口函数比子查询更合理
直接用 SUM() 窗口函数实现累计求和,是现代 SQL 的标准做法;子查询虽能达成效果,但性能差、可读性低、容易出错。如果你的数据库支持窗口函数(PostgreSQL、SQL Server、Oracle、MySQL 8.0+、SQLite 3.25+),就别硬写子查询。
子查询累计求和的典型写法(仅限不得已时)
核心思路:对每一行,用相关子查询统计「当前行及之前所有行」的值之和。假设表 sales 有字段 date 和 amount,按日期累计:
SELECT s1.date, s1.amount, (SELECT SUM(s2.amount) FROM sales s2 WHERE s2.date <p>注意点:</p>
- 必须确保
WHERE条件能唯一锚定“之前”的范围——用date不够稳妥,若存在同日多条记录,需叠加主键或时间戳细化排序逻辑 - 子查询里不能引用外部查询的非关联字段(如
s1.id以外的列),否则报错或结果错乱 - 没有索引时,N 行数据会触发 N 次全表扫描,复杂度 O(N²),万级数据就明显卡顿
MySQL 5.7 或旧版 SQLite 怎么办?
这些版本不支持窗口函数,又不想改架构,只能靠变量模拟(MySQL)或自连接(通用但慢):
MySQL 变量方案(依赖执行顺序,慎用于复杂 JOIN):
SET @cum := 0; SELECT date, amount, (@cum := @cum + amount) AS cumsum FROM sales ORDER BY date;
风险提示:
-
ORDER BY必须显式写出,且不能被优化器重排(例如嵌套在子查询里可能失效) - 变量赋值在 SELECT 中属于未定义行为(MySQL 官方文档明确标注),高并发或升级后易出问题
- 无法在视图或某些 ORM 查询中安全使用
为什么 GROUP BY + 自连接容易算错?
常见错误写法:
SELECT s1.date, SUM(s2.amount) AS cumsum FROM sales s1 JOIN sales s2 ON s2.date <p>问题在于:</p>
- 若
date不唯一,会产生笛卡尔积,同一笔amount被重复累加多次 - 没处理 NULL 值:只要任意
s2.amount为 NULL,整组SUM()就返回 NULL - 没加
ORDER BY,结果顺序不可靠,而累计和天然依赖顺序
真正要落地,优先确认数据库版本和权限——能开窗口函数就别碰子查询;真被卡在老环境,变量方案比自连接稍可控,但上线前务必用边界数据(含空值、重复时间、单日多笔)压测。










