很多開發者知道 GROUP BY 後只能選分組列或聚合函數,但不清楚本質原因。本文引入「虛擬分組表」思維模型,將分組過程可視化,一次性講透分組邏輯、多字段分組、聚合函數角色與常見報錯根因。
| id | name | number |
|---|---|---|
| 1 | aa | 2 |
| 2 | aa | 3 |
| 3 | bb | 5 |
| 4 | cc | 7 |
SELECT name FROM test GROUP BY name 的三階段模型得到原始表,無變化。
將 name 相同的行合併為一行,非分組列的值收集為集合放入同一單元格:
| name (分組鍵) | id (集合) | number (集合) |
|---|---|---|
| aa | {1, 2} | {2, 3} |
| bb | {3} | {5} |
| cc | {4} | {7} |
關鍵洞察:關系數據庫要求單元格原子性(單值),虛擬表中 id、number 單元格包含多值,不符合關系模型,故不能直接 SELECT *。
SELECT name:分組鍵列,每格單值 → 合法。SELECT id / SELECT number:非分組鍵,多值 → 非法(報錯:not a GROUP BY expression)。SELECT name, SUM(number):聚合函數輸入多值集合,輸出單值 → 合法。
SUM({2,3}) = 5,結果表:| name | SUM(number) |
|---|---|
| aa | 5 |
| bb | 5 |
| cc | 7 |
GROUP BY name, number 等價於按 (name, number) 元組分組:
| (name, number) | id (集合) |
|---|---|
| (aa, 2) | {1} |
| (aa, 3) | {2} |
| (bb, 5) | {3} |
| (cc, 7) | {4} |
此時 name 和 number 單元格均為單值(因為它們是分組鍵的一部分),可直接 SELECT;id 仍需聚合。
示例:SELECT name, SUM(id) FROM test GROUP BY name, number
| name | SUM(id) |
|---|---|
| aa | 1 |
| aa | 2 |
| bb | 3 |
| cc | 4 |
| 函數 | 輸入 | 輸出 | 別名 |
|---|---|---|---|
| COUNT | 集合大小 | 整數 | 行數 |
| SUM | 數值集合 | 總和 | 求和 |
| AVG | 數值集合 | 平均值 | 平均 |
| MAX/MIN | 可比較集合 | 極值 | 極值 |
| GROUP_CONCAT | 字符串集合 | 拼接字符串 | 列轉行 |
| 錯誤 SQL | 報錯原因 | 修正方案 |
|---|---|---|
SELECT * FROM t GROUP BY a | 非分組列多值 | 列出分組列 + 聚合函數 |
SELECT a, b FROM t GROUP BY a | b 非分組列非聚合 | SELECT a, MAX(b) 或 GROUP BY a, b |
SELECT a, COUNT(*) FROM t GROUP BY a, b | 語法合法,但語義可能不符預期 | 確認是否需 GROUP BY a |