max()和min()忽略null,整列null时返回null;查整行需子查询或order by+limit;where过滤优于having;无索引时全表扫描,性能差。

用 MAX() 和 MIN() 查单列极值,但别忘了 NULL 会自动被忽略
这两个函数只对非 NULL 值计算,如果整列都是 NULL,结果就是 NULL。常见误判是看到返回 NULL 就以为没数据,其实可能是数据本身为空或类型不匹配(比如字符串字段存了空串 '',它不等于 NULL,但 MIN() 仍会参与比较)。
- 数值型字段放心用:
SELECT MAX(price), MIN(price) FROM products; - 字符串按字典序比:
MIN(name)返回字母最靠前的值,不是长度最短的 - 日期字段也适用:
MAX(created_at)得到最后时间,前提是字段是DATETIME或DATE类型,别用VARCHAR存时间
查带条件的极值,WHERE 比 HAVING 更安全
HAVING 是分组后的过滤,容易在没 GROUP BY 时出错或逻辑绕弯。要查“2023 年销量最高的商品”,直接用 WHERE 先筛年份,再套 MAX(),而不是先聚合再过滤。
- ✅ 正确:
SELECT MAX(sales) FROM orders WHERE YEAR(order_date) = 2023; - ❌ 危险:
SELECT MAX(sales) FROM orders HAVING YEAR(order_date) = 2023;—— 这句语法可能通过,但语义错误,HAVING无法引用未聚合的order_date - 注意:如果条件涉及聚合结果(如“平均单价 > 100 的品类中找最高价”),才需要
HAVING配合GROUP BY
想查整行记录(比如价格最高的那条商品),MAX() 不能直接返回其他字段
MAX(price) 只返回价格数值,不会顺带把对应商品的 name、id 一起捞出来。这是初学者最常卡住的地方——以为加个 SELECT id, name, MAX(price) 就能拿到完整记录,实际会报错或返回不可预测的 id(取决于 SQL 模式,比如 ONLY_FULL_GROUP_BY 开启时直接拒绝执行)。
- ✅ 推荐做法:用子查询定位极值,再关联原表
SELECT * FROM products WHERE price = (SELECT MAX(price) FROM products); - ✅ 替代方案:用
ORDER BY ... LIMIT 1(注意有并列最高值时只取一条)SELECT * FROM products ORDER BY price DESC LIMIT 1; - ⚠️ 避免:
SELECT id, name, MAX(price) FROM products;—— 在严格模式下会失败,在宽松模式下id和name值无业务意义
性能要注意:极值查询没索引时会全表扫描
MAX() 和 MIN() 在有对应索引的字段上可以走索引 B+ 树的最左/最右叶子节点,几乎 O(1);但如果没有索引,就得扫完整张表。尤其当表有千万级数据、又频繁查最新时间或最高金额时,慢得明显。
- 检查是否命中索引:
EXPLAIN SELECT MAX(updated_at) FROM logs;看type是否为index或range - 复合索引要注意顺序:如果常用
WHERE status = 1 ORDER BY created_at DESC再取极值,建索引优先考虑(status, created_at),而不是反过来 -
MIN(id)在主键上天然快,但MIN(non_indexed_column)就是硬扫
事情说清了就结束。极值本身简单,难的是搞清它和上下文的关系——在哪过滤、怎么取整行、有没有索引兜底。这几个点漏一个,线上就容易卡住或返回错数据。










