有一表 t2(id PK, a),數據:(1,10), (3,1)。
-- 返回 1 行
SELECT * FROM t2 HAVING id = MIN(id); -- id=1
-- 返回空集
SELECT * FROM t2 HAVING id = MAX(id); -- 預期 id=3,實際空集
-- 同理 a 列
SELECT * FROM t2 HAVING a = MAX(a); -- 返回 a=10 (id=1)
SELECT * FROM t2 HAVING a = MIN(a); -- 返回空集
單獨使用 HAVING 不符合 SQL 標準,MySQL 內部將其重寫為:
SELECT * FROM t2 HAVING id = MIN(id)
-- 重寫為
SELECT * FROM t2 GROUP BY NULL HAVING id = MIN(id)
SELECT * FROM t2 GROUP BY NULL;
-- 等價於 LIMIT 1,只返回第一行數據
+------+----+
| a | id |
+------+----+
| 10 | 1 |
+------+----+
關鍵點:MIN/MAX 等聚合函數計算的是全表所有行,而非 GROUP BY 後的單組數據。
SELECT MAX(a), MIN(a), MAX(id), MIN(id) FROM t2 GROUP BY NULL;
+--------+--------+---------+---------+
| MAX(a) | MIN(a) | MAX(id) | MIN(id) |
+--------+--------+---------+---------+
| 10 | 1 | 3 | 1 |
+--------+--------+---------+---------+
HAVING id = MIN(id) → HAVING id = 1 → 第一行 id=1 滿足 → 返回 1 行HAVING id = MAX(id) → HAVING id = 3 → 第一行 id=1 不滿足 → 返回空集HAVING a = MAX(a) → HAVING a = 10 → 第一行 a=10 滿足 → 返回 1 行HAVING a = MIN(a) → HAVING a = 1 → 第一行 a=10 不滿足 → 返回空集-- 加上 GROUP BY 列,聚合函數在組內計算,結果一致
SELECT MAX(a), MIN(a), MAX(id), MIN(id) FROM t2 GROUP BY a;
+--------+--------+---------+---------+
| MAX(a) | MIN(a) | MAX(id) | MIN(id) |
+--------+--------+---------+---------+
| 1 | 1 | 3 | 3 |
| 10 | 10 | 1 | 1 |
+--------+--------+---------+---------+
GROUP BY NULL 等價 LIMIT 1,取第一行-- 取最小 id 的行
SELECT * FROM t2 ORDER BY id ASC LIMIT 1;
-- 取最大 id 的行
SELECT * FROM t2 ORDER BY id DESC LIMIT 1;
-- 8.0+ 窗口函數
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY a ORDER BY id) AS rn
FROM t2
) t WHERE rn = 1;
「無 GROUP BY 直接 HAVING」看似能跑通,實則是「第一行數據值」與「全表聚合值」巧合相等的副作用。理解 GROUP BY NULL ≡ LIMIT 1 及聚合函數計算範圍,即可避開此坑。寫 SQL 時請遵循標準語法:WHERE 過濾行 → GROUP BY 分組 → HAVING 過濾組。