想象一下:你在关注你公司的收入、网店的销售,或者只是分析自己一年的花销。你不光想看到每个月的收入或支出,还想明白它们是怎么一个月一个月累积起来的。
普通的聚合函数(GROUP BY)帮不上忙——它们会把数据分组,每组只返回一行。可如果你想看到每个月并且还要算累计和呢?这时候就得用 SUM() 搭配窗口函数了。
用窗口函数算累计和的基础
窗口函数让你可以在窗口范围内做聚合操作。这样我们就能在每一行上累加数值,但又不会把其他行删掉。再也不用为 GROUP BY 牺牲数据啦!
SUM() 配合窗口函数的语法
这是用来算累计和的基本模板:
SELECT
column_name,
SUM(column_name) OVER (PARTITION BY partition_column ORDER BY order_column) AS cumulative_sum
FROM
table_name;
这里:
SUM(column_name)—— 累加数值。OVER()—— 定义窗口范围。PARTITION BY—— 按组分区(可选)。ORDER BY—— 定义窗口内的行顺序。
例子:每月累计收入
假设有一张你的收入表:
| 月份 | 收入 |
|---|---|
| 2023-01 | 1000 |
| 2023-02 | 1500 |
| 2023-03 | 2000 |
我们想看到每个月的收入和累计总和。来写个 SQL 查询:
SELECT
月份,
收入,
SUM(收入) OVER (ORDER BY 月份) AS 累计收入
FROM
收入表;
结果:
| 月份 | 收入 | 累计收入 |
|---|---|---|
| 2023-01 | 1000 | 1000 |
| 2023-02 | 1500 | 2500 |
| 2023-03 | 2000 | 4500 |
发生了什么:
ORDER BY 月份在OVER()里告诉 PostgreSQL 要按时间顺序处理行。- 每一行的累计和都是把所有之前的行(包括当前行)都加起来。
仔细想想这里的逻辑。第一行 SUM() 只算第一行,第二行算前两行,第三行算前三行。所以月份排序很重要!
例子:分地区累计收入
如果你有一张按地区分的销售表,部分数据可能是这样的:
| 地区 | 月份 | 收入 |
|---|---|---|
| 北方 | 2023-01 | 1000 |
| 北方 | 2023-02 | 1500 |
| 南方 | 2023-01 | 2000 |
| 南方 | 2023-02 | 2500 |
现在我们想分别算每个地区的累计收入:
SELECT
地区,
月份,
收入,
SUM(收入) OVER (PARTITION BY 地区 ORDER BY 月份) AS 累计收入
FROM
销售表;
结果会是这样:
| 地区 | 月份 | 收入 | 累计收入 |
|---|---|---|---|
| 北方 | 2023-01 | 1000 | 1000 |
| 北方 | 2023-02 | 1500 | 2500 |
| 南方 | 2023-01 | 2000 | 2000 |
| 南方 | 2023-02 | 2500 | 4500 |
现在每个地区都单独分析(PARTITION BY 地区),但每个地区内部还是按时间排序(ORDER BY 月份)。
滑动平均(AVG())
OK,累计和很酷,但如果你想分析最近 3 个月的趋势呢?这时候滑动平均就派上用场了。
例子:收入滑动平均
我们还是用 收入表,数据如下:
| 月份 | 收入 |
|---|---|
| 2023-01 | 1000 |
| 2023-02 | 1500 |
| 2023-03 | 2000 |
| 2023-04 | 2500 |
计算 3 个月滑动平均的查询:
SELECT
月份,
收入,
AVG(收入) OVER (
ORDER BY 月份
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS 滑动平均
FROM
收入表;
结果:
| 月份 | 收入 | 滑动平均 |
|---|---|---|
| 2023-01 | 1000 | 1000 |
| 2023-02 | 1500 | 1250 |
| 2023-03 | 2000 | 1500 |
| 2023-04 | 2500 | 2000 |
解释:
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW告诉 PostgreSQL 取当前行和前两行来算平均。- 所以每个月都能看到最近 3 个月的平均收入。
也就是说,每一行都用 3 行(当前+前两行)做窗口,然后算平均。超级方便。
ORDER BY 的作用和影响
窗口函数很依赖行的顺序。如果顺序错了(或者根本没指定),结果可能会很奇怪。
例子:没写 ORDER BY 的错误
如果我们把 OVER() 里的 ORDER BY 去掉,累计和就会变成每行都是所有收入的总和:
SELECT
月份,
收入,
SUM(收入) OVER () AS 错误累计和
FROM
收入表;
结果:
| 月份 | 收入 | 错误累计和 |
|---|---|---|
| 2023-01 | 1000 | 7000 |
| 2023-02 | 1500 | 7000 |
| 2023-03 | 2000 | 7000 |
| 2023-04 | 2500 | 7000 |
行没排序,函数就直接把所有行加起来,每行都一样,根本不是累计和。
真实用法场景
收入分析:
- 累计和能帮你追踪公司销售或收入的增长。
- 滑动平均能让你看到“干净”的趋势,去掉噪音。
金融建模:
银行和金融公司用窗口函数分析还款、负债增长等各种指标。
时间序列分析:
像在线用户数、页面浏览量、营收这些时间数据,用 SUM() 和 AVG() 分析最合适不过了。
GO TO FULL VERSION