GROUP BY 后必须出现 SELECT 中的所有非聚合字段
MySQL 8.0+ 默认启用
sql_mode=ONLY_FULL_GROUP_BY,这意味着如果
SELECT列表里有非聚合字段(比如
name、
status),它们必须显式出现在
GROUP BY子句中,否则报错:
Expression #1 of SELECT list is not in GROUP BY clause。
常见错误写法:
SELECT id, name, COUNT(*) FROM users GROUP BY status;
这在 MySQL 5.7 之前可能“侥幸”通过,但结果不可靠——
id和
name取的是哪一行的值?MySQL 不保证。8.0+ 直接拒绝执行。 正确做法:把所有非聚合列都放进
GROUP BY,例如
GROUP BY status, name或改用聚合函数包裹,如
MAX(id)、
ANY_VALUE(name)(需确认业务逻辑是否允许) 不建议临时关掉
ONLY_FULL_GROUP_BY,它是为了防止隐式歧义而设的
WHERE 和 HAVING 的分工不能颠倒
WHERE过滤行,
HAVING过滤分组,这是关键区别。很多人误把条件全塞进
HAVING,导致性能下降甚至逻辑错误。
比如查“每种状态的用户数超过 5 人的活跃用户”:
SELECT status, COUNT(*) AS cnt FROM users WHERE is_active = 1 GROUP BY status HAVING cnt > 5;
is_active = 1必须放
WHERE:提前过滤,减少参与分组的数据量
cnt > 5必须放
HAVING:因为
cnt是分组后才计算出的别名,
WHERE看不见它 若把
cnt > 5错写成
WHERE COUNT(*) > 5,会直接报错:
Invalid use of group function
ORDER BY 中引用聚合结果要小心别名和字段位置
GROUP BY查询中,
ORDER BY可以用列别名、序号或表达式,但不同 MySQL 版本行为略有差异。
例如:
SELECT status, COUNT(*) AS total FROM users GROUP BY status ORDER BY total DESC;用别名
total排序是安全且可读的,推荐 用位置序号如
ORDER BY 2虽然合法,但一旦调整
SELECT列顺序就容易出错 避免在
ORDER BY中重复写聚合表达式,比如
ORDER BY COUNT(*),虽然可行,但冗余且难维护 注意:MySQL 允许
ORDER BY引用未出现在
SELECT中的字段(只要在
GROUP BY里),但这种写法依赖隐式分组语义,易引发理解偏差
多字段分组时注意 NULL 值的分组行为
当
GROUP BY包含多个字段,且其中某些值为
NULL,MySQL 把所有
NULL视为“相同”,归入同一组。这和
WHERE col = NULL永远为 false 不同,但容易被忽略。
例如:
SELECT dept, level, COUNT(*) FROM employees GROUP BY dept, level;如果
dept或
level有
NULL,它们各自形成独立的
NULL组(即
(NULL, 'senior')、
('tech', NULL)、(NULL, NULL)是三个不同组) 但单个字段为
NULL的所有行,会全部聚到该字段对应的那个
NULL组里 若想排除
NULL再分组,得加
WHERE dept IS NOT NULL AND level IS NOT NULL用
COALESCE(dept, 'unknown')替换
NULL是常见补救手段,但要注意它改变了原始分组语义 实际写
GROUP BY时,最常卡住的地方不是语法,而是没想清楚“我要按什么维度聚合”和“这个维度的空值代表什么”。这两个问题没理清,后面加再多
HAVING或
ORDER BY都只是在掩盖逻辑漏洞。
