用MAX()和MIN()可直接提取单列极值,自动忽略NULL;需查整行记录时应用窗口函数如RANK()或子查询;注意NULL处理、数据库版本差异及数据类型隐式转换问题。

用 MAX() 和 MIN() 直接提取单个极值
最直接的办法就是用聚合函数 MAX() 和 MIN()。它们不返回整行,只返回指定列的单一最大或最小值,适合做统计摘要。
比如查成绩表里最高分和最低分:
SELECT MAX(score) AS max_score, MIN(score) AS min_score FROM report;
注意:这两个函数会自动忽略 NULL 值;如果整列都是 NULL,结果也是 NULL。别指望它告诉你哪一行对应这个极值——那是另一类问题。
- 不能用在没有
GROUP BY的查询中同时选普通列和MAX(),否则报错(除非该列是函数参数或常量) - 对字符串字段也能用:
MAX(name)按字典序取“最大”字符串,但业务意义通常很弱 - 性能上基本无压力,只要字段有索引,
MAX()/MIN()常能走索引最左/最右叶节点,O(1) 级别完成
查出“哪一行”的极大值记录(非聚合场景)
当你要的不是数值本身,而是“张三的数学成绩最高那条记录”,就得跳出聚合思维,用窗口函数或关联子查询。
推荐用 RANK() 或 ROW_NUMBER() 配合 OVER():
SELECT * FROM ( SELECT *, RANK() OVER (ORDER BY score DESC) AS rk FROM report ) t WHERE rk = 1;
这会把所有并列最高分的行都拉出来。ROW_NUMBER() 则强制唯一编号,适合只要一条。
-
RANK()并列时跳号(比如两个第1名,下一个就是第3名);DENSE_RANK()不跳号;按需选 - 如果要“每个学生最高分的课程”,必须加
PARTITION BY id,否则是全局排名 - 旧版 MySQL(5.7 及之前)不支持窗口函数,得用自连接或相关子查询,性能差、写法绕
处理 NULL 值对极值判断的干扰
MAX() 和 MIN() 默认跳过 NULL,看起来省心,但有时你得明确知道“有没有 NULL 出现过”。比如监控字段是否全为空,或者想把 NULL 当作极小值参与排序。
这时不能依赖默认行为,得显式控制:
- 检查是否存在
NULL:COUNT(*) != COUNT(score)表示有空值 - 把
NULL当最小值排:ORDER BY score ASC NULLS FIRST(PostgreSQL / Oracle / MySQL 8.0+ 支持) - 兼容老版本(如 SQL Server 2019):用
ORDER BY CASE WHEN score IS NULL THEN 0 ELSE 1 END DESC, score DESC - 慎用
COALESCE(score, -999999)替代——补的极小值可能和真实业务极小值冲突,且破坏索引有效性
达梦、SQL Server 等国产/传统数据库的特殊注意点
国产数据库如达梦(DM),或较老的 SQL Server 版本,在极值计算上容易踩隐性坑。
比如达梦的 DECIMAL 字段若定义为 DECIMAL(10,2),插入 99999999.999 就会触发 -6102: 数据溢出 错误——这不会在 MAX() 计算时暴露,而是在原始数据 INSERT 阶段就失败。所以极值异常往往源头在数据写入约束,而非查询逻辑。
- 查
MAX()返回值异常低?先确认源字段是否有隐式类型转换或截断(比如VARCHAR存数字,MAX()按字符串比) - SQL Server 2016 及以前不支持
NULLS LAST,写ORDER BY score DESC NULLS LAST会直接语法报错 - 分区表上查极值,若没落在查询条件覆盖的分区里,
MAX()可能返回NULL(尤其用了WHERE但分区键没进条件)
真正麻烦的从来不是“怎么写出极值”,而是“为什么这个极值看起来不对”——往往卡在数据类型定义、NULL 处理策略、或数据库版本特性上。动手前先 SELECT VERSION() 和 DESCRIBE table_name 看一眼,比硬调 SQL 更省时间。

















