SQL 使用窗口函数计算累计值

发布时间 - 2026-01-24 00:00:00    点击率:
累计求和必须用ORDER BY,否则结果不可靠;正确写法为SUM(amount) OVER(PARTITION BY user_id ORDER BY create_time ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),并注意索引、空值和重复值处理。

累计求和必须用 ORDER BY,否则结果不可靠

窗口函数如 SUM() OVER() 计算累计值时,ORDER BY 不是可选的——它定义了“累计”的顺序。没有 ORDER BY,数据库会按任意物理顺序累加,同一查询多次执行可能返回不同结果。

常见错误写法:SUM(amount) OVER (PARTITION BY user_id) —— 这只是分组内总和,不是累计值。

  • 正确写法必须带排序:SUM(amount) OVER (PARTITION BY user_id ORDER BY create_time)
  • 时间字段含重复值时,建议补一个唯一列(如 id)避免非确定性:ORDER BY create_time, id
  • 若需升序累计(最早→最新),用默认升序;降序累计(最新→最早)则显式写 ORDER BY create_time DESC

ROWS BETWEEN UNBOUNDED PRECEDING AND

CURRENT ROW
是默认行为,但显式写出更安全

很多教程省略帧子句,依赖默认的 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW。这在大多数场景下成立,但某些数据库(如老版本 PostgreSQL 或特定执行计划下)可能表现不一致。

  • 显式声明能杜绝歧义:SUM(amount) OVER (PARTITION BY user_id ORDER BY create_time ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
  • 不要用 RANGE 帧(如 RANGE BETWEEN...),它对时间/数值型排序键容易因重复值导致“跳加”,产生意外累计结果
  • 如果真要跳过当前行(比如计算“截至上一行”的累计),改用 ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING

空值(NULL)会被自动跳过,但要注意排序字段为 NULL 的行位置

SUM() 窗口函数天然忽略 NULL 值,这点和普通聚合一致。真正容易出错的是排序字段本身为 NULL:不同数据库对 NULLS FIRST/NULLS LAST 默认处理不同。

  • PostgreSQL 默认 NULLS FIRST,MySQL 8.0+ 默认 NULLS LAST,结果可能错位
  • 稳妥做法是显式声明:ORDER BY create_time NULLS LAST(假设你想把有效时间排前面)
  • 如果业务上不允许 create_time 为空,建表时应加 NOT NULL 约束,比运行时处理更可靠

性能敏感场景下,避免在大表上对非索引字段 ORDER BY

累计值计算本质是按序扫描,若 ORDER BY 字段无索引,数据库可能触发全表排序(External Sort),I/O 和内存开销陡增。

  • 检查执行计划中是否出现 Sort 节点且 Rows Removed by Filter 很高
  • 复合索引优先考虑:(user_id, create_time) —— 同时支撑分区与排序
  • 如果只是查最近 N 条的累计(如“最近 30 天用户每日充值累计”),先用子查询或 CTE 限定数据范围,再套窗口函数,比全表扫快得多
实际用的时候,最常掉坑的地方不是函数写错,而是忘了排序字段有没有索引、空值怎么排、以及重复时间戳要不要加二级排序。这些细节不提前对齐,上线后查半天才发现累计数隔几天就变一次。


# mysql  # sql  # NULL  # sort  # Filter  # postgresql  # 数据库  # 升序  # 跳过  # 不可靠  # 的是  # 子句  # 几天  # 半天  # 很高  # 才发现  # 这只 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 原生JS实现图片轮播切换效果  今日头条微视频如何找选题 今日头条微视频找选题技巧【指南】  香港服务器租用每月最低只需15元?  Laravel怎么导出Excel文件_Laravel Excel插件使用教程  Laravel如何实现用户角色和权限系统_Laravel角色权限管理机制  如何正确下载安装西数主机建站助手?  浅述节点的创建及常见功能的实现  Linux系统运维自动化项目教程_Ansible批量管理实战  黑客如何通过漏洞一步步攻陷网站服务器?  如何在阿里云服务器自主搭建网站?  高性价比服务器租赁——企业级配置与24小时运维服务  Laravel如何使用集合(Collections)进行数据处理_Laravel Collection常用方法与技巧  Laravel如何实现密码重置功能_Laravel密码找回与重置流程  php 三元运算符实例详细介绍  如何用花生壳三步快速搭建专属网站?  零基础网站服务器架设实战:轻量应用与域名解析配置指南  猎豹浏览器开发者工具怎么打开 猎豹浏览器F12调试工具使用【前端必备】  Laravel怎么发送邮件_Laravel Mail类SMTP配置教程  如何在服务器上配置二级域名建站?  如何实现javascript表单验证_正则表达式有哪些实用技巧  如何用狗爹虚拟主机快速搭建网站?  Laravel如何集成Inertia.js与Vue/React?(安装配置)  Laravel如何实现多级无限分类_Laravel递归模型关联与树状数据输出【方法】  Python3.6正式版新特性预览  javascript读取文本节点方法小结  如何在腾讯云服务器上快速搭建个人网站?  Win11任务栏卡死怎么办 Windows11任务栏无反应解决方法【教程】  如何在不使用负向后查找的情况下匹配特定条件前的换行符  laravel怎么为应用开启和关闭维护模式_laravel应用维护模式开启与关闭方法  微信小程序 canvas开发实例及注意事项  Swift中switch语句区间和元组模式匹配  详解Android中Activity的四大启动模式实验简述  如何用西部建站助手快速创建专业网站?  如何利用DOS批处理实现定时关机操作详解  ,交易猫的商品怎么发布到网站上去?  Laravel 419 page expired怎么解决_Laravel CSRF令牌过期处理  java中使用zxing批量生成二维码立牌  WordPress 子目录安装中正确处理脚本路径的完整指南  头像制作网站在线观看,除了站酷,还有哪些比较好的设计网站?  深圳网站制作的公司有哪些,dido官方网站?  简历在线制作网站免费版,如何创建个人简历?  使用spring连接及操作mongodb3.0实例  Laravel Livewire是什么_使用Laravel Livewire构建动态前端界面  海南网站制作公司有哪些,海口网是哪家的?  黑客如何利用漏洞与弱口令入侵网站服务器?  Laravel模型关联查询教程_Laravel Eloquent一对多关联写法  详解Android图表 MPAndroidChart折线图  Laravel怎么使用Session存储数据_Laravel会话管理与自定义驱动配置【详解】  laravel怎么实现图片的压缩和裁剪_laravel图片压缩与裁剪方法  Win11怎么关闭资讯和兴趣_Windows11任务栏设置隐藏小组件