《夜に駆ける》- 不是回頭統計「哪一天最痛」,而是在情緒奔跑的過程中,痛苦自然浮到最前面

MAX + JOIN 的人生像在事後寫失戀報告,假設你用 MAX + GROUP BY + JOIN 來寫這首歌,列出所有相處的日子、快樂指數、痛苦指數、聊天次數,GROUP BY「每一天」並算出「痛苦指數 MAX 的那一天」再回頭 JOIN 那一天的對話、場景、畫面最後下結論「原來是那一天最痛。」

  • 痛苦 = MAX
  • 回憶 = JOIN
  • 情緒 = 被拆成資料再拼回來

這會寫成一篇 Excel 報告,不會是一首歌

a

而 Window Function 實際在做的是,情緒一路推進,畫面一路疊加,痛苦不斷「排序往前」

「我在跑的時候,就知道逃不掉。」

1
2
3
4
ROW_NUMBER() OVER (
PARTITION BY 這段關係
ORDER BY 情緒崩壞程度 DESC
)

INTO_THE_NIGHT

MAX => GROUPBY

1
2
3
4
5
6
7
8
SELECT o.*
FROM Orders o
JOIN (
SELECT CustomerId, MAX(OrderDate) AS MaxDate
FROM Orders
GROUP BY CustomerId
) latest ON o.CustomerId = latest.CustomerId
AND o.OrderDate = latest.MaxDate

先不談效能,只看「這個寫法為什麼會出現」的話,需求本身其實很單純「我要每個 CustomerId 最新的一筆訂單」

這時候考慮使用 GROUPBY 會發現,有一個硬限制,你只能選 GROUP BY 欄位聚合結果(MAX / SUM / COUNT),不能同時拿到那一整列的其他欄位

kk

所以第一步只能退而求其次:先算答案,每個 CustomerId 的「最大 OrderDate」,這一步只是在回答最新日期是什麼?

但我們要的是 OrderId、Amount、Status,這些在 GROUP BY 後全部消失了,因此被迫做第二步:JOIN 回原始表,用「剛算出的答案」回去找「符合這個答案的那一筆資料」,造成整體流程是先算出正確答案,再用答案去反推資料

a

所以其實是在用 GROUP BY 做一件它不擅長的事 (╥﹏╥)

a

Window Function 的本質是「在不破壞資料列的前提下,直接在資料流中計算分組結果」,而不是算完再回頭補資料

1
2
3
4
5
6
7
SELECT *
FROM (
SELECT *,
RANK() OVER (PARTITION BY CustomerId ORDER BY OrderDate DESC) AS RankNo
FROM Orders
) ranked
WHERE RankNo = 1

oo

c

資料表只被掃描一次,沒有先縮資料、也沒有產生中間結果表,每一列資料都還「活著」,PARTITION BY 不是 GROUP BY,排序與計算在同一個資料流中完成,ORDER BY 定義分組內的順序,RANK 在排序過程中即時計算,不需要「算完再比對」,最後只做一次條件篩選,沒有任何資料回查或比對行為

aa

  • 少一次掃描
  • 少一次 JOIN
  • 少一個中間結果集
  • 更容易順著索引走
面向 傳統 GROUP BY + JOIN Window Function
資料掃描次數 至少 2 次 (聚合、回連) 1 次
需排序或 Hash Hash Aggregate + Hash Join 單一排序(可用索引)
中間結果大小 聚合結果 + JOIN 結果 直接輸出
ties 處理 需額外 DISTINCT 或 TOP 可直接控制(ROW_NUMBER / RANK)
Optimizer 可調度性 較複雜 更可預測、記憶體使用穩定

z

aa