想像一下:你在追蹤你公司的收入、網店的銷售,或是單純分析你一整年的花費。你不只想看到每個月的收入或支出,還想知道這些數字是怎麼一個月一個月累積起來的。
一般的聚合函數(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,累積總和很酷,但如果你想分析最近三個月的趨勢呢?這時候就要用移動平均啦!
範例:收入的移動平均
我們還是用 收入表,資料如下:
| 月份 | 收入 |
|---|---|
| 2023-01 | 1000 |
| 2023-02 | 1500 |
| 2023-03 | 2000 |
| 2023-04 | 2500 |
計算三個月移動平均的查詢:
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 要看當前行和前兩行來算平均。- 所以每個月都會看到最近三個月的平均收入。
也就是說,每一行都設定一個三行的視窗:自己加上前兩行,然後計算平均。超方便!
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