乍看之下,窗口函式跟聚合函式好像都是拿來分析跟處理資料的工具。畢竟兩者都能做像是 sum、avg、排名這種計算。但我們來細看一下,實際上它們本質上有什麼差別。
聚合函式(GROUP BY)
聚合函式的運作方式如下:
- 它們會根據你指定的欄位把資料分組。
- 分組之後,每一組只會剩下一列結果。
- 舉例:你想知道每個地區的總收入。
SELECT region, SUM(sales) AS total_sales
FROM sales_data
GROUP BY region;
特點:GROUP BY 會「壓縮」資料。如果你用了分組,屬於同一組的所有列都會消失——只剩下聚合的結果。
窗口函式(PARTITION BY)
窗口函式則是這樣:
- 會保留原本的資料結構(不會壓縮或讓列消失!)。
- 可以在「窗口」——也就是邏輯上分開的分組裡做計算。
舉例:你想知道每個城市在它所屬地區的總銷售額佔比,但又想保留所有資料。
SELECT
region,
city,
sales,
SUM(sales) OVER (PARTITION BY region) AS total_sales_by_region
FROM sales_data;
特點:用窗口函式不會刪掉任何列,只是把新的計算值加到每一列上。
範例:SUM() 搭配 GROUP BY vs SUM() 搭配 PARTITION BY
為了更清楚差異,我們來看 SUM() 在兩種情境下怎麼運作。假設我們有一張 sales_data 表長這樣:
| region | city | sales |
|---|---|---|
| North | CityA | 100 |
| North | CityB | 150 |
| South | CityC | 200 |
| South | CityD | 250 |
用 GROUP BY 做加總
我們想知道每個地區的總銷售額:
SELECT region, SUM(sales) AS total_sales
FROM sales_data
GROUP BY region;
結果會長這樣:
| region | total_sales |
|---|---|
| North | 250 |
| South | 450 |
發生了什麼:資料根據 region 分組,每組被「壓縮」成一列,顯示銷售總和。
用 PARTITION BY 做加總
現在我們用窗口函式做一樣的事:
SELECT
region,
city,
sales,
SUM(sales) OVER (PARTITION BY region) AS total_sales_by_region
FROM sales_data;
結果:
| region | city | sales | total_sales_by_region |
|---|---|---|---|
| North | CityA | 100 | 250 |
| North | CityB | 150 | 250 |
| South | CityC | 200 | 450 |
| South | CityD | 250 | 450 |
發生了什麼:PARTITION BY 沒有「壓縮」資料,而是在每個「窗口」(每個地區)裡計算總和,然後把結果加到每一列。
什麼時候該用 GROUP BY,什麼時候該用 PARTITION BY?
GROUP BY:適合做最終報表
GROUP BY 很適合你想要縮減資料量、只看分組結果的時候。像是:
- 每個月的總銷售額。
- 每個商品類別的訂單數。
範例:
SELECT category, COUNT(*) AS total_orders
FROM orders
GROUP BY category;
PARTITION BY:超適合分析跟細部觀察
PARTITION BY 適合你想保留所有資料列、又想對每一列多算點東西的時候。像是:
- 算出每個商品在類別裡的銷售佔比。
- 在每個分組裡給資料編號。
計算銷售佔比的範例:
SELECT
category,
product,
sales,
ROUND(
(sales * 100.0) / SUM(sales) OVER (PARTITION BY category),
2
) AS sales_percentage
FROM sales_data;
範例:同時用多個窗口函式
窗口函式一大優點就是可以一次做很多計算。舉例:
SELECT
region,
city,
sales,
SUM(sales) OVER (PARTITION BY region) AS total_sales,
RANK() OVER (PARTITION BY region ORDER BY sales DESC) AS sales_rank
FROM sales_data;
結果:
| region | city | sales | total_sales | sales_rank |
|---|---|---|---|---|
| North | CityB | 150 | 250 | 1 |
| North | CityA | 100 | 250 | 2 |
| South | CityD | 250 | 450 | 1 |
| South | CityC | 200 | 450 | 2 |
窗口函式比 GROUP BY 更強的地方
保留原始資料: GROUP BY 會壓縮資料列,但窗口函式可以讓你保留原本的表格結構。
一個查詢多種計算: 你可以在同一個查詢裡用不同參數的 PARTITION BY 跟 ORDER BY 做多種窗口函式計算,資料都還在。
分析超彈性: 窗口函式可以讓你根據需求做累積總和、排名、比例計算等等,超級彈性。
彈性範例
我們來組合幾個函式:
SELECT
region,
city,
sales,
SUM(sales) OVER (PARTITION BY region) AS total_sales,
AVG(sales) OVER (PARTITION BY region) AS avg_sales,
RANK() OVER (PARTITION BY region ORDER BY sales DESC) AS rank
FROM sales_data;
結果:
| region | city | sales | total_sales | avg_sales | rank |
|---|---|---|---|---|---|
| North | CityB | 150 | 250 | 125.0 | 1 |
| North | CityA | 100 | 250 | 125.0 | 2 |
| South | CityD | 250 | 450 | 225.0 | 1 |
| South | CityC | 200 | 450 | 225.0 | 2 |
限制跟常見錯誤
一個很常見的錯誤是想用 PARTITION BY 來「壓縮」資料。像是,應該這樣寫:
SELECT region, SUM(sales) AS total_sales
FROM sales_data
GROUP BY region;
但有人會寫成這樣:
SELECT
region,
SUM(sales) OVER (PARTITION BY region) AS total_sales
FROM sales_data;
但這樣會回傳所有資料列,資料量一點都沒減少(有時候這不是你想要的)。
現在你一定知道什麼時候該用 GROUP BY,什麼時候該用窗口函式了。這就像選擇要用鐵鎚還是螺絲起子:兩個工具都能處理釘子……但方式不一樣啦。
GO TO FULL VERSION