MAX()和MIN()是聚合函数,必须配合GROUP BY使用(除非整表只取一个值),单独SELECT会返回全表极值,但易受NULL、数据类型隐式转换和空结果集影响。

直接说结论:MAX() 和 MIN() 是聚合函数,必须配合 GROUP BY 使用(除非整表只取一个值),单独 SELECT 它们会返回全表范围的极值——但很多人卡在 NULL 处理、数据类型隐式转换和空结果集这三处。
为什么 SELECT MAX(id) 返回 NULL?
常见于字段本身允许 NULL 且全为 NULL,或 WHERE 条件没匹配到任何行。MySQL 和 PostgreSQL 行为一致:MAX() 在空结果集时返回 NULL,不是 0 或空字符串。
- 检查字段是否真有非 NULL 值:
SELECT COUNT(*) FROM table WHERE column IS NOT NULL - 避免 NULL 干扰极值判断:用
COALESCE(MAX(column), 0)提供默认值(注意类型兼容性) - WHERE 条件写错会导致结果为空——比如
WHERE status = 'active'但实际值是'ACTIVE'或含空格
日期字段用 MIN() / MAX() 却得到错误结果?
本质是字符串比较而非时间比较。比如 created_at 存的是 '2023-1-5'(缺前导零),MAX(created_at) 可能返回 '2023-12-31' 但实际比 '2023-1-10' 小,因为字符串比较停在第二个字符 '2' vs '1'。
- 确认字段类型是
DATETIME或DATE,不是VARCHAR - 若无法改类型,强制转换:
MAX(STR_TO_DATE(created_at, '%Y-%m-%d'))(MySQL)或MAX(created_at::DATE)(PostgreSQL) - ISO 格式
'YYYY-MM-DD'字符串可安全比较,但带时间或不规范格式一律不建议依赖
想查“每个用户最新订单时间”,却只得到一个 MAX() 值?
漏写了 GROUP BY。聚合函数作用域默认是整结果集,要按用户分组就得显式声明:
SELECT user_id, MAX(created_at) AS latest_order FROM orders GROUP BY user_id;
如果还想要对应订单的其他字段(比如 order_id),MAX() 本身做不到——它只返回极值,不保留整行。这时候得用窗口函数或关联子查询:
- 窗口函数(推荐,性能好):
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC)筛出每组第一行 - 关联子查询:
WHERE created_at = (SELECT MAX(created_at) FROM orders o2 WHERE o2.user_id = orders.user_id) - 注意:MySQL 5.7 不支持窗口函数,得降级用子查询或升级版本
最常被忽略的其实是数据分布——比如 MAX(price) 在千万级表上没索引,执行一次就可能锁表几秒;而 MIN(id) 如果 id 是主键,引擎直接走 B+ 树最左/最右节点,快得多。别只盯着函数怎么写,先看执行计划。

















