在企业财务数据统计场景中,经常需要计算每个季度的累计利润,也就是当前季度的利润加上之前所有季度的利润总和。使用SQL的窗口函数结合时间分区可以高效实现这个需求,不需要复杂的自连接或者子查询逻辑。

基础数据准备
首先我们需要有一张存储季度利润的基础表,假设表名为quarterly_profit,表结构如下:
| 字段名 | 类型 | 说明 |
|---|---|---|
| quarter_id | INT | 季度标识,比如202301代表2023年第一季度 |
| profit | DECIMAL(10,2) | 当前季度的利润金额 |
表中存储的示例数据如下:
| quarter_id | profit |
|---|---|
| 202301 | 10000.00 |
| 202302 | 15000.00 |
| 202303 | 12000.00 |
| 202304 | 18000.00 |
| 202401 | 20000.00 |
核心实现逻辑
要实现按时间分区计算累计利润,核心是使用SUM()窗口函数,配合PARTITION BY和ORDER BY子句:
PARTITION BY用来按年份分区,保证累计利润只在同一年度内计算,不会跨年累加ORDER BY用来按季度顺序排序,保证累计的顺序是从第一季度到第四季度- 窗口函数的范围是
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,也就是从分区内的第一行到当前行,默认就是这个范围,可以省略
完整SQL示例
下面是完整的查询语句,会返回每个季度的利润以及对应的累计利润:
-- 计算每个季度的累计利润
SELECT
quarter_id,
profit,
-- 按年份分区,按季度排序,计算累计利润
SUM(profit) OVER (
PARTITION BY SUBSTRING(CAST(quarter_id AS CHAR), 1, 4)
ORDER BY quarter_id
) AS cumulative_profit
FROM quarterly_profit
ORDER BY quarter_id;
上述代码中,SUBSTRING(CAST(quarter_id AS CHAR), 1, 4)的作用是提取quarter_id的前四位作为年份,实现按年分区。如果quarter_id的存储格式不同,比如是2023-Q1这种格式,可以调整分区逻辑,比如用LEFT(quarter_id, 4)提取年份。
结果说明
上述查询的执行结果如下:
| quarter_id | profit | cumulative_profit |
|---|---|---|
| 202301 | 10000.00 | 10000.00 |
| 202302 | 15000.00 | 25000.00 |
| 202303 | 12000.00 | 37000.00 |
| 202304 | 18000.00 | 55000.00 |
| 202401 | 20000.00 | 20000.00 |
可以看到2023年的累计利润是逐季度累加的,到2024年第一季度时,累计利润重新从20000开始计算,符合按年分区的需求。
注意事项
实际使用中需要注意以下几点:
- 如果季度数据存在缺失,比如某一年只有前两个季度的数据,累计逻辑依然会正常执行,只会累加已有的季度数据
- 如果profit字段存在NULL值,SUM函数会自动忽略NULL值,如果需要把NULL当作0处理,可以用
COALESCE(profit, 0)转换 - 如果季度标识不是按数字顺序排列,比如是字符串格式的季度名,需要先转换成可排序的格式,再放到ORDER BY子句中
窗口函数的执行效率比传统的自连接写法更高,尤其是在数据量较大的场景下,建议优先使用窗口函数实现累计计算需求。