判斷聚合函數(如 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也能得到正確結果,但屬於冗餘寫法)。

三、避坑要點

  1. 數據庫嚴格模式的強制要求

    • 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;
      
  2. 不要「為了不報錯」濫用聚合函數 比如明明分組內字段唯一,卻習慣性加MAX: → 雖然結果正確,但會降低SQL可讀性,也可能讓維護者誤解「該字段分組內有多個值」。

  3. 關聯表字段的特殊處理 多表關聯+分組時,關聯表的字段需先判斷「關聯字段是否唯一」:

    • 若T1.SEC_ID → T2.SEC_ID是「一對一」:關聯表字段無需聚合;
    • 若T1.SEC_ID → T2.SEC_ID是「一對多」:關聯表字段必須聚合。

四、總結判斷口訣

  1. 先看GROUP BY:確定結果行的粒度;
  2. 再看字段:分組內多值→必須聚合,分組內唯一→無需聚合;
  3. 關聯表字段:先看關聯關係(一對一/一對多),再按上述規則判斷。

回到你的原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;