標準 SQL 要求:SELECT 列表中非聚合欄位**必須**出現在 GROUP BY 中。但 MySQL 預設允許:
SELECT id, COUNT(1), aa FROM t GROUP BY id;
-- aa 未在 GROUP BY,MySQL 返回該組第一行的 aa 值(非確定性)
風險:結果不可預測,不同版本/配置可能不同。生產**嚴禁**依賴此行為。
SET sql_mode = 'ONLY_FULL_GROUP_BY';
-- 或 my.cnf 永久生效:
[mysqld]
sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'
-- 方案 1:aa 加入 GROUP BY
SELECT id, COUNT(1), aa FROM t GROUP BY id, aa;
-- 方案 2:aa 用聚合函數
SELECT id, COUNT(1), MAX(aa) FROM t GROUP BY id;
-- 方案 3:子查詢先聚合再關聯詳情
SELECT t1.id, t1.cnt, t2.aa
FROM (SELECT id, COUNT(1) AS cnt FROM t GROUP BY id) t1
JOIN t t2 ON t1.id = t2.id
LIMIT 1; -- 取任意一行
| 特性 | WHERE | HAVING |
|---|---|---|
| 執行順序 | 分組前過濾 | 分組後過濾 |
| 可用聚合函數 | 否 | 是 |
| 典型場景 | 原始行過濾 | 聚合結果過濾 |
-- 原始表:student, subject, score
-- 每生總分 > 180
SELECT student, SUM(score) AS total
FROM scores
GROUP BY student
HAVING SUM(score) > 180;
-- 等價於(但效能較差)
SELECT student, total FROM (
SELECT student, SUM(score) AS total FROM scores GROUP BY student
) t WHERE total > 180;
HAVING score = 80:若 score 未聚合,MySQL 允許但語義模糊(取組內第一行),應改 HAVING MAX(score) = 80 或 HAVING MIN(score) = 80ONLY_FULL_GROUP_BY