怎样在SQL中使用SUM OVER计算账户余额的实时变动

秋明大大_2783

秋明大大_2783

2026-10-08

137人浏览

原创

直接用 sum() over() 算余额易出错,因未设期初余额且 created_at 排序不唯一;须显式添加期初行、order by created_at, id,并按业务类型条件聚合,同时建立 account_id, created_at, id 联合索引。

怎样在sql中使用sum over计算账户余额的实时变动

为什么直接用 SUM() OVER() 算余额容易出错

很多人一上来就写 SUM(amount) OVER (ORDER BY created_at),结果发现余额对不上——不是漏了初始余额,就是没处理同一时间多笔交易的排序不确定性。窗口函数本身不关心业务语义,它只按指定顺序累加,而银行账户余额必须满足两个硬约束:① 起点是期初余额(不是 0),② 同一毫秒的多笔操作必须有确定顺序(否则重放结果不一致)。

常见错误现象:SUM() OVER() 返回值比手工核对少一笔、余额突然跳变、导出数据每次运行结果不同。

  • 必须显式加入期初余额行(比如用 UNION ALL 插入一条 amount = 10000 的记录,并设 created_at = '2024-01-01 00:00:00')
  • 排序字段不能只有 created_at,要补上唯一列(如 id 或 transaction_id)避免并列时非确定性:ORDER BY created_at, id
  • 如果表里有撤销/冲正交易(amount 为负),确保它们已归入同一张明细表,而不是靠应用层过滤后计算

SUM() OVER() 的 ORDER BY 必须包含唯一键

MySQL 8.0+、PostgreSQL、SQL Server 都支持窗口函数,但只要 ORDER BY 子句中存在重复值,数据库就可能任意打乱相同 created_at 的行顺序。这意味着你昨天跑出的余额序列,今天再跑可能中间几行数字就变了——尤其在批量导入或日志回放场景下极危险。

正确做法是把业务主键或自增 ID 加进排序:

SELECT
  id,
  created_at,
  amount,
  10000 + SUM(amount) OVER (ORDER BY created_at, id) AS balance
FROM account_transactions
WHERE status = 'success';

注意:这里 10000 是期初余额,硬编码仅适用于简单场景;生产环境建议从另一张 account_summary 表查出 opening_balance 并 JOIN 进来。

如何处理「先记账后确认」类交易(如冻结、解冻)

真实账户系统常有“可用余额”和“实际余额”之分,比如转账时先冻结资金(产生 type = 'freeze' 记录),到账后再确认(type = 'settle')。这时不能把所有 amount 无差别累加。

你需要按业务类型做条件聚合:

  • 用 CASE WHEN 过滤只计入影响实际余额的操作:SUM(CASE WHEN type IN ('deposit', 'withdrawal', 'settle') THEN amount ELSE 0 END)
  • 若需同时看可用余额,可另起一列:SUM(CASE WHEN type IN ('deposit', 'withdrawal', 'unfreeze') THEN amount ELSE 0 END)
  • 避免在 WHERE 中过滤掉冻结类记录——否则窗口排序会丢失位置,导致后续 settle 行的累计值错位

性能隐患:大表上 SUM() OVER(ORDER BY ...) 会全表扫描

当 account_transactions 超过百万行,且没有合适索引时,ORDER BY created_at, id 可能触发 filesort,执行计划显示 Using temporary; Using filesort。这不是语法问题,而是优化器找不到覆盖索引。

必须建联合索引:

CREATE INDEX idx_acc_trans_order ON account_transactions (account_id, created_at, id);

注意三点:

  • account_id 放最左,因为查询一定带账户维度过滤(否则算的是全平台余额)
  • 不要只建 (created_at, id)——缺少 account_id 会导致索引无法用于 WHERE account_id = ?
  • 如果经常按日期范围查(如最近 7 天),确保 created_at 在索引中位置靠前,让 range scan 生效

窗口函数本身无法下推谓词,所以 WHERE 条件越早过滤掉无关账户和状态,性能越好。

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

4063

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

871

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

1049

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

5941

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

2843

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

5920

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

7901

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1070

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

932

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.6万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.4万人学习