max()必须配合group by才能查每组最大值,单独使用只返回全表最大值;漏写group by会导致报错或结果错位,且无法获取对应完整记录,需用窗口函数或子查询实现。

MAX() 必须配合 GROUP BY 才能查每组最大值
单独写 SELECT MAX(price) FROM products 只会返回全表最大值,不是“每组”。想按分类、日期、用户等维度取最大值,GROUP BY 是硬性前提,漏掉就得不到分组结果。
常见错误是写成:SELECT category, MAX(price) 却没加 GROUP BY category —— 大多数数据库(如 MySQL 5.7+、PostgreSQL、SQL Server)会直接报错:ERROR 1055 或类似提示;MySQL 5.6 可能返回不可靠的随机行。
- 必须明确写出
GROUP BY字段,且该字段要出现在SELECT列表中(除非用函数包裹) - 如果还想查对应最大值的其他字段(比如哪个商品最贵),
MAX()本身做不到,得换用窗口函数或子查询 - 注意 NULL 值:
MAX()自动忽略 NULL,但如果整组全是 NULL,结果为 NULL
查“每组最大值 + 对应完整记录”不能只靠 MAX()
MAX() 只返回聚合值,不保留原始行上下文。比如想查每个 category 中价格最高的那条商品记录(含 name、id 等),下面写法是错的:
SELECT category, MAX(price), name FROM products GROUP BY category
因为 name 不在 GROUP BY 里,也不被聚合函数包裹,MySQL 8.0+ 和标准 SQL 都会拒绝执行。
- 正确做法之一:用窗口函数
ROW_NUMBER() OVER (PARTITION BY category ORDER BY price DESC),再过滤序号 = 1 的行 - 兼容老版本(如 MySQL 5.7)可用关联子查询:
WHERE (category, price) IN (SELECT category, MAX(price) FROM products GROUP BY category) - 注意并列情况:如果有多个商品价格同为最高,
ROW_NUMBER()只取一个,RANK()或DENSE_RANK()才能保留全部
不同数据库对 MAX() 的 NULL 和类型处理有差异
MAX() 在各库行为基本一致,但细节容易踩坑:
- 字符串类型的
MAX(name)按字典序比较,不是按长度——'zebra'>'apple',但'10''2'(因为字符比较) - PostgreSQL 要求
GROUP BY列必须和SELECT中所有非聚合列完全一致;MySQL 允许“功能依赖”,但开启ONLY_FULL_GROUP_BY模式后也强制严格匹配 - SQLite 的
MAX()对 BLOB 返回 NULL,对 NULL 字段返回 NULL,行为稳定;但不支持窗口函数(直到 3.25.0+ 才有限支持)
性能提醒:MAX() 在无索引字段上会触发全表扫描
如果对没有索引的字段(比如 description)用 MAX() 分组,数据库无法快速定位最大值,只能扫完整个分组数据。实际慢查中,这类语句常出现在报表定时任务里。
- 优化关键:确保
GROUP BY字段 +MAX()字段存在联合索引,例如INDEX(category, price) - 避免在
MAX()内部套函数,比如MAX(UPPER(name))—— 索引失效,且无法利用排序优势 - 如果只是判断“是否存在大于某值的记录”,用
EXISTS或WHERE price > X比MAX() > X更快
分组聚合看着简单,但一牵扯到“带上下文取整行”或跨版本兼容,就得仔细看执行计划。别假设 MAX() 总是高效,尤其当分组键基数低、单组数据量大时,索引设计比写法更重要。











