NULL默认位置不固定:PostgreSQL/Oracle/SQL Server默认ASC时排末尾、DESC时排最前,MySQL 5.7+默认ASC时排开头;必须用NULLS FIRST/LAST显式控制或CASE兜底确保跨库一致性。

ORDER BY 中 NULL 值默认排在哪?
SQL 标准规定:NULL 被视为“未知值”,不等于任何值(包括它自己),在排序时**不参与大小比较**,而是由数据库实现决定其位置。绝大多数主流数据库(PostgreSQL、SQL Server、Oracle、SQLite)默认将 NULL 视为**最大值**,即 ORDER BY col ASC 时 NULL 排在最后,DESC 时排在最前。MySQL 是个例外:5.7 及之前版本默认把 NULL 当作**最小值**(ASC 时在最前),8.0+ 已对齐标准,但受 sql_mode 和存储引擎影响,行为仍可能不一致。
所以不能依赖“默认”,必须显式控制。
用 NULLS FIRST / NULLS LAST 显式指定位置
这是 SQL:2003 标准语法,PostgreSQL、Oracle、SQL Server(2012+)、SQLite(3.30+)都支持:
-
ORDER BY col ASC NULLS FIRST → NULL 永远排最前,无论升序降序
-
ORDER BY col DESC NULLS LAST → NULL 永远排最后
- 注意:
NULLS FIRST/LAST 必须写在方向关键字(ASC 或 DESC)之后,顺序不能颠倒
ORDER BY col ASC NULLS FIRST → NULL 永远排最前,无论升序降序ORDER BY col DESC NULLS LAST → NULL 永远排最后NULLS FIRST/LAST 必须写在方向关键字(ASC 或 DESC)之后,顺序不能颠倒示例:
SELECT name, score FROM students ORDER BY score DESC NULLS LAST;
确保高分在前,没考的(score IS NULL)统一垫底,不会意外飘到顶部。
MySQL 和旧版 SQLite 怎么办?
MySQL(尤其 5.7 及更早)和老 SQLite(NULLS FIRST/LAST,得用表达式绕过:
- 让
NULL 变成一个极小/极大占位值,再排序:
ORDER BY IFNULL(col, -999999) DESC(适用于数值)
- 更通用安全的做法是用
CASE:
ORDER BY CASE WHEN col IS NULL THEN 1 ELSE 0 END, col DESC
→ 先按是否为 NULL 分组(NULL → 1,非空 → 0),再按原字段排,这样 NULL 就能稳居末尾
- 字符串字段同理:
ORDER BY CASE WHEN name IS NULL THEN 'zzzzzz' ELSE name END,但要注意字符集和 collation 影响排序结果
NULL 变成一个极小/极大占位值,再排序:ORDER BY IFNULL(col, -999999) DESC(适用于数值)CASE:ORDER BY CASE WHEN col IS NULL THEN 1 ELSE 0 END, col DESC→ 先按是否为
NULL 分组(NULL → 1,非空 → 0),再按原字段排,这样 NULL 就能稳居末尾ORDER BY CASE WHEN name IS NULL THEN 'zzzzzz' ELSE name END,但要注意字符集和 collation 影响排序结果注意:IFNULL() 和 COALESCE() 在索引上通常无法高效下推,可能导致全表扫描;而 CASE WHEN ... IS NULL 方式在某些场景下可利用索引(取决于优化器),但需实测执行计划。
ORDER BY 多字段时 NULL 行为会叠加吗?
会,但只作用于对应字段。比如:ORDER BY a ASC NULLS LAST, b DESC NULLS FIRST,a 字段的 NULL 全部沉底,同一组 a 非空值里,b 的 NULL 又会浮到该小组顶部。
容易被忽略的是:如果两个字段都有大量 NULL,且你没指定 NULLS 子句,不同数据库行为差异会直接导致分页结果错乱(比如第一页末尾和第二页开头出现重复或跳行)。线上分页查询涉及 ORDER BY 时,只要字段可能为 NULL,就必须显式声明 NULLS FIRST/LAST 或等效逻辑。

















