判斷聚合函數(如 SUM/MAX/MIN/COUNT/AVG 等)是否需要使用,核心原則是:**當一行分組結果需要彙總/提煉分組內多行數據的特徵時,必須用聚合函數;若分組內該字段值唯一,無需使用(用了也不報錯,但冗餘)**。
以下從「判斷邏輯」「典型場景」「避坑要點」三個維度詳細説明,結合你之前的SQL案例拆解:
一、核心判斷邏輯(兩步法)
第一步:明確「分組粒度」(GROUP BY 決定)
GROUP BY 後的字段組合定義了「一行結果對應的業務維度」,比如你之前的 GROUP BY PORT_ID, SEC_ID, PORT_NAME,表示最終結果的每一行對應「一個PORT_ID + 一個SEC_ID + 一個PORT_NAME」的組合。
第二步:判斷字段在分組內的「唯一性」
| 字段特徵 | 是否需要聚合函數 | 原因 |
|---|---|---|
| 分組內字段值完全相同 | 不需要 | 該字段在當前分組下只有一個值,直接取即可(聚合函數如MAX/MIN也能取到,但冗餘) |
| 分組內字段值有多個 | 必須需要 | 需通過聚合函數將多行值提煉為一個值(如SUM求和、MAX取最大值、COUNT計數) |
| 關聯表的字段(如T2.SEC_NAME) | 看關聯關係 | 若關聯字段(SEC_ID)是關聯表的主鍵/唯一鍵 → 分組內唯一,無需聚合;否則需聚合 |
二、典型場景舉例(結合你的SQL)
場景1:SUM(必須用)
你的SQL中 SUM(T1.CHANGE_VALUE) AS 實際持倉數量:
- 分組粒度:PORT_ID + SEC_ID + PORT_NAME;
- 分組內特徵:同一個PORT_ID+SEC_ID下可能有多條CHANGE_VALUE記錄(比如多次持倉變更);
- 結論:必須用SUM——需要把分組內所有CHANGE_VALUE加總,得到該維度下的總持倉數量。
場景2:MAX(T2.SEC_NAME)(是否用看關聯關係)
- 情況A:DWD_AST_INFO表中SEC_ID是主鍵(一個SEC_ID對應唯一SEC_NAME); → 分組內SEC_NAME唯一,無需MAX,直接寫
T2.SEC_NAME即可; - 情況B:DWD_AST_INFO表中SEC_ID不是唯一鍵(一個SEC_ID對應多個SEC_NAME); → 分組內SEC_NAME有多個值,必須用聚合函數(MAX/MIN/GROUP_CONCAT),否則數據庫會報錯(嚴格模式下)。
場景3:MAX(T1.SEC_TYPE)(冗餘,可去掉)
你的SQL中 MAX(T1.SEC_TYPE):
- 分組粒度包含T1.SEC_ID,而T1.SEC_TYPE是SEC_ID的屬性(一個SEC_ID對應唯一SEC_TYPE);
- 結論:分組內SEC_TYPE值唯一,無需MAX,直接寫
T1.SEC_TYPE即可(用MAX也能得到正確結果,但屬於冗餘寫法)。
三、避坑要點
-
數據庫嚴格模式的強制要求
- MySQL(ONLY_FULL_GROUP_BY模式)、PostgreSQL、Oracle等,要求SELECT中的字段必須滿足: ✅ 要麼在GROUP BY中; ✅ 要麼被聚合函數包裹。
- 反例(報錯):
SELECT PORT_ID, SEC_ID, SEC_NAME -- SEC_NAME不在GROUP BY,也無聚合函數 FROM TAG_PTL_SEC_CHANGE GROUP BY PORT_ID, SEC_ID;
-
不要「為了不報錯」濫用聚合函數 比如明明分組內字段唯一,卻習慣性加MAX: → 雖然結果正確,但會降低SQL可讀性,也可能讓維護者誤解「該字段分組內有多個值」。
-
關聯表字段的特殊處理 多表關聯+分組時,關聯表的字段需先判斷「關聯字段是否唯一」:
- 若T1.SEC_ID → T2.SEC_ID是「一對一」:關聯表字段無需聚合;
- 若T1.SEC_ID → T2.SEC_ID是「一對多」:關聯表字段必須聚合。
四、總結判斷口訣
- 先看GROUP BY:確定結果行的粒度;
- 再看字段:分組內多值→必須聚合,分組內唯一→無需聚合;
- 關聯表字段:先看關聯關係(一對一/一對多),再按上述規則判斷。
回到你的原SQL,優化後合理的寫法是:
SELECT
CONCAT(T1.PORT_ID, '#', T1.SEC_ID) AS `KEY`,
T1.PORT_NAME,
T2.SEC_NAME, -- 若SEC_ID是T2主鍵,直接取,無需MAX
T1.SEC_TYPE, -- 分組內唯一,無需MAX
T1.SEC_TYPE2,
T1.SEC_TYPE3,
SUM(T1.CHANGE_VALUE) AS `實際持倉數量` -- 分組內多值,必須SUM
FROM TAG_PTL_SEC_CHANGE T1
INNER JOIN DWD_AST_INFO T2 ON T1.SEC_ID = T2.SEC_ID
WHERE ...
GROUP BY T1.PORT_ID, T1.SEC_ID, T1.PORT_NAME, T1.SEC_TYPE, T1.SEC_TYPE2, T1.SEC_TYPE3;