空值在排序里的位置
排序时空值算大还是算小,两种数据库的默认约定正好相反:MySQL 把空值当成极小值,升序时排在最前面;PostgreSQL 把空值当成极大值,升序时排在最后面。更要紧的是语法支持不同:PostgreSQL 可以用显式子句指定空值排前还是排后,MySQL 没有这个语法,只能用表达式构造一个排序键来变通。这个差异会让同一套报表在两个数据库上输出的首尾行完全不同。
基本信息
| 适用平台 | MySQL 8.0 / PostgreSQL 12 及以上 |
|---|---|
| MySQL 默认 | 空值视为极小值,升序在前、降序在后 |
| PostgreSQL 默认 | 空值视为极大值,升序在后、降序在前 |
| PostgreSQL 语法 | 支持显式指定空值排在前面或后面 |
| MySQL 变通 | 用判断空值的表达式构造排序键 |
典型使用场景
- 排行榜展示:未填资料的用户不应该排在榜首
- 报表导出:缺失值统一放到末尾,便于人工核对
- 分页一致性:显式指定空值位置,避免两库输出顺序不一致
- 从 MySQL 迁移到 PostgreSQL 后核对排序结果
- 待办列表排序:未设置的截止时间排在最后
两库写法
两库写法
-- PostgreSQL:显式指定空值位置,语义清楚
SELECT * FROM t ORDER BY col ASC NULLS LAST;
SELECT * FROM t ORDER BY col DESC NULLS FIRST;
-- MySQL 变通一:先按「是否为空」排,再按值排
SELECT * FROM t ORDER BY col IS NULL, col ASC;
-- 说明:判断结果为 0 或 1,非空的排前面
-- MySQL 变通二:用分支表达式构造排序键,可读性更好
SELECT * FROM t
ORDER BY CASE WHEN col IS NULL THEN 1 ELSE 0 END, col;
-- 两库通用写法:用 COALESCE 把空值替换成一个业务上合理的值
SELECT * FROM t ORDER BY COALESCE(col, '9999-12-31') ASC;空值位置对照
| 数据库 | 升序时空值位置 | 降序时空值位置 |
|---|---|---|
| MySQL | 最前(视为极小值) | 最后 |
| PostgreSQL | 最后(视为极大值) | 最前 |
| PostgreSQL 显式指定排后 | 最后 | 最后 |
| PostgreSQL 显式指定排前 | 最前 | 最前 |
| MySQL 变通加判断键 | 按表达式决定 | 按表达式决定 |
从 MySQL 迁到 PostgreSQL 之后,报表的首尾行常常会变。对顺序敏感的场景(榜单、导出对账文件)一定要显式指定空值位置,不要依赖默认约定。
用判断表达式构造排序键的写法比较绕,容易被后续维护者误改。建议在同一行写清注释说明意图,或者干脆统一封装成视图供多处复用。