GROUP BY + MAX() 是最直接的写法,用于按字段分组后取每组最大值,但需配合 GROUP BY 使用,否则返回全表最大值;漏写 GROUP BY 或 SELECT 非聚合列未加入 GROUP BY 会报错;若需完整行,应使用 ROW_NUMBER() 窗口函数或关联子查询;WHERE 中不可用 MAX(),须用 HAVING;MAX() 跳过 NULL,整组为 NULL 时结果为 NULL,可用 COALESCE 兜底。

GROUP BY + MAX() 是最直接的写法
想按某字段分组后取每组最大值,MAX() 必须配合 GROUP BY 使用,不能只写 SELECT MAX(col) FROM table —— 那只会返回整张表的最大值,不是“每组”。
常见错误是漏掉 GROUP BY,或者把非聚合列(比如 id、name)直接放进 SELECT 却没加在 GROUP BY 里,MySQL 5.7+ 和 PostgreSQL 会报错:ERROR 1055 或 column must appear in the GROUP BY clause。
- 正确写法:
SELECT category, MAX(price) FROM products GROUP BY category - 如果还想查出对应那条记录的
id或name,MAX()本身做不到 —— 它只返回值,不保留原始行上下文 - 注意:
MAX()对字符串也有效,按字典序比较,比如MAX('apple', 'banana')返回'banana'
要获取完整行(比如最大价格对应的商品名),别用 MAX() 直接拼字段
很多人试图这样写:SELECT category, MAX(price), name FROM products GROUP BY category,结果 name 值是随机的(MySQL 兼容模式下可能侥幸对,但不可靠)。
真正安全的做法是用窗口函数或关联子查询:
- 推荐用
ROW_NUMBER()窗口函数(MySQL 8.0+、PostgreSQL、SQL Server 都支持):SELECT category, name, price<br>FROM (<br> SELECT *, ROW_NUMBER() OVER (PARTITION BY category ORDER BY price DESC) rn<br> FROM products<br>) t<br>WHERE rn = 1;
- 兼容老版本(如 MySQL 5.6)可用相关子查询:
SELECT p1.category, p1.name, p1.price FROM products p1 WHERE p1.price = (SELECT MAX(p2.price) FROM products p2 WHERE p2.category = p1.category) - 注意:如果有多个商品价格并列最高,
ROW_NUMBER()只取一个,RANK()或DENSE_RANK()才能保留全部
MAX() 在 WHERE 中不能直接用,得用子查询或 HAVING
想筛选“最大值大于 100 的分组”,不能写 WHERE MAX(price) > 100 —— 这会报错:Invalid use of aggregate function。因为 WHERE 执行在分组前,而 MAX() 是分组后计算的。
- 正确方式是用
HAVING:SELECT category, MAX(price) FROM products GROUP BY category HAVING MAX(price) > 100 - 如果条件涉及其他表或更复杂逻辑,可先用子查询算出各组最大值,再
JOIN或WHERE过滤 -
HAVING只能跟在GROUP BY后,且只能用聚合函数或GROUP BY中的列
性能和 NULL 处理容易被忽略
MAX() 会自动跳过 NULL 值,但如果整组都是 NULL,结果就是 NULL —— 不是 0 或空字符串。这在统计报表里常导致数据断层。
- 需要默认值时,用
COALESCE(MAX(price), 0)显式兜底 - 大表上
GROUP BY + MAX()性能依赖category和price是否有联合索引,比如(category, price)能显著加速 - 如果只是查单个分组的最大值(比如
WHERE category = 'book'),加普通索引category就够了,不必覆盖price
真正麻烦的是既要分组又要取完整行,这时候窗口函数写法清晰,但旧版数据库只能硬扛子查询;另外,MAX() 看似简单,一牵扯到“哪一行”就立刻变成行级语义问题,得换思路处理。

















