max/min不能在where中直接使用,因where在聚合前执行;正确写法是子查询或窗口函数;group by后筛选须用having;null值会被跳过,全null返回null;有索引时性能极佳。

MAX 和 MIN 在 WHERE 子句里直接用不了
很多人一上来就想写 SELECT * FROM orders WHERE price = MAX(price),结果报错:ERROR 1111 (HY000): Invalid use of group function。这不是函数写错了,而是 SQL 执行顺序决定的:WHERE 在聚合计算之前执行,根本看不到 MAX() 的结果。
真正能用的写法只有两种:
- 用子查询先算出极值,再拿去比较:
SELECT * FROM orders WHERE price = (SELECT MAX(price) FROM orders) - 用窗口函数(MySQL 8.0+ / PostgreSQL / SQL Server)避免重复扫描:
SELECT * FROM (SELECT *, MAX(price) OVER() AS max_price FROM orders) t WHERE price = max_price
注意:子查询方式在大数据表上可能慢,因为要扫两次表;窗口函数更高效,但老版本 MySQL 不支持。
GROUP BY 后的 MAX/MIN 容易误读为“整表最大”
写 SELECT category, MAX(price) FROM products GROUP BY category 是对的,但有人会接着加 WHERE MAX(price) > 100,这又会触发同样的错误。聚合后的筛选必须用 HAVING,不是 WHERE。
常见混淆点:
-
WHERE过滤的是“行”,作用于分组前 -
HAVING过滤的是“组”,作用于GROUP BY之后,可以放心用MAX()、COUNT()等 - 如果没写
GROUP BY却用了HAVING,MySQL 允许(隐式全表为一组),但 PostgreSQL 会报错,别依赖这个行为
NULL 值会让 MAX/MIN “消失”,但不是报错
MAX() 和 MIN() 会自动跳过 NULL,这点和 SUM() 一致。但如果整列全是 NULL,结果就是 NULL,而不是 0 或空字符串。
实际影响:
- 用在
ORDER BY时:ORDER BY MAX(updated_at) DESC如果某组没数据,排序字段为NULL,这类记录会排最前(MySQL 默认)或最后(PostgreSQL),行为不统一 - 和
COALESCE配合更稳妥:COALESCE(MAX(price), 0)显式兜底 - 时间字段慎用:
MAX(created_at)返回NULL可能掩盖数据缺失问题,建议加WHERE created_at IS NOT NULL显式过滤
性能差往往是因为没走索引,而不是函数本身
MAX() 和 MIN() 在有合适索引时能秒出结果——比如对 price 建了 B-Tree 索引,查 MAX(price) 实际只取索引最右叶子节点,不用扫全表。
但以下情况会失效:
- 对表达式用函数:
MAX(price * 1.1)无法利用price索引 - 字段上有函数包装:
MAX(ABS(price))同样跳过索引 - 联合索引顺序不匹配:如果索引是
(category, price),那MAX(price) WHERE category = 'book'能用索引;但去掉WHERE条件查全表MAX(price)就只能走索引扫描(仍比全表快),而MAX(category)则完全用不上这个索引
查执行计划时重点看 type 是否为 index 或 range,而不是 ALL。










