sum() over()是财务报表累计发生额最稳妥解法,必须指定partition by account_code和order by account_date, id并显式声明rows帧,以防日期重复导致累计错乱。

财务报表里怎么用SUM() OVER()做累计发生额
财务最常卡在“本月累计”“本年累计”这类计算上,直接用GROUP BY会丢掉明细行,用子查询又嵌套太深。正确做法是用SUM() OVER()加明确的排序和范围。
关键点:必须指定ORDER BY,且字段得是能体现时间先后的(比如account_date),否则结果不可靠;默认窗口是RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,对日期字段可能出错——相同日期多笔分录时会把当天所有行都算进来。
- 稳妥写法:
SUM(amount) OVER (PARTITION BY account_code ORDER BY account_date, id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)—— 加id防日期重复导致累计错乱 - 如果要“按月累计”,别只
ORDER BY YEAR(account_date), MONTH(account_date),得先生成唯一序号再排序,否则同月多笔仍会乱序 - MySQL 8.0+ 和 PostgreSQL 支持
ROWS帧,SQL Server 也支持;但 Oracle 默认用RANGE,日期重复时行为不同,上线前务必验证
环比/同比怎么写才不出错:LAG()的空值和偏移陷阱
财务分析绕不开“比上月涨了多少”“比去年同期增减”,LAG()是主力,但两个坑踩中一个就全崩:空值没处理、偏移量设错。
LAG(amount, 1) OVER (ORDER BY account_date)取上一行,但首行一定是NULL;如果后面直接除或减,整列变NULL。更隐蔽的是:按自然月算环比,不能简单偏移1行——2月只有28天,3月有31天,第29行根本不是“上月同日”。
- 强制补零:
COALESCE(LAG(amount, 1) OVER (ORDER BY account_date), 0),避免后续计算报错 - 按日历对齐同比:
LAG(amount, 365) OVER (ORDER BY account_date)仅适用于平年,闰年需用DATE_SUB(account_date, INTERVAL 1 YEAR)关联,而非偏移行数 - 月份级环比建议用
LAG(amount) OVER (PARTITION BY YEAR(account_date), MONTH(account_date) ORDER BY account_date)不成立——得先聚合到月粒度再拉偏移
科目余额表怎么取“最新一条余额”而不漏数据
余额表不是静态快照,而是每笔凭证更新后生成新行,要取每个account_code下account_date最大的那条。很多人写ROW_NUMBER() OVER (PARTITION BY account_code ORDER BY account_date DESC)后WHERE rn = 1,结果发现某些科目没了——因为account_date相同、id不同,ORDER BY不稳定,rn分配随机。
- 必须加确定性排序:
ROW_NUMBER() OVER (PARTITION BY account_code ORDER BY account_date DESC, id DESC) - 别用
RANK()或DENSE_RANK()——它们对并列值给相同排名,会导致多行rn = 1,余额重复 - 如果表里有
update_time字段,优先用它代替id,更贴近业务含义 - 注意:MySQL 5.7 不支持窗口函数,执行会直接报
FUNCTION ROW_NUMBER does not exist,先查SELECT VERSION()
为什么ORDER BY字段没索引,财务月报跑10分钟?
窗口函数本身不建临时表,但OVER()里的ORDER BY会触发全量排序。一张500万行的凭证表,account_date没索引,SUM() OVER (ORDER BY account_date)就会拖慢整个查询——PostgreSQL执行计划里出现Sort节点,MySQL 8.0+ 的EXPLAIN FORMAT=TREE显示window_function + filesort。
- 复合索引要严格匹配:
(account_code, account_date)才能加速PARTITION BY account_code ORDER BY account_date - 单列索引
account_date对纯时间排序有效,但加了PARTITION BY后效果打折 - 财务系统常有“按期间查询”,索引字段顺序必须是分区字段在前、排序字段在后,反了就用不上
真正容易被忽略的不是语法,是窗口函数执行阶段——它在GROUP BY之后、HAVING之前运行,但又不能引用未出现在SELECT或GROUP BY里的列。写完先看执行计划,再查版本兼容性,最后验数据稳定性。











