首页/数据/SQL 速查/ORDER BY 排序与稳定性

ORDER BY 排序与稳定性

对结果排序:支持多列、表达式、列序号与别名;没有 ORDER BY 时返回顺序不保证,分页与 Top N 场景必须显式排序。

SQL 速查数据更新于 2026-10-13

ORDER BY 是什么

ORDER BY 对结果排序,支持多列、表达式、列别名与 SELECT 列序号。它位于 SELECT 之后,所以能引用别名;位于 LIMIT 之前,所以分页与 Top N 必须先排序。没有 ORDER BY 时返回顺序不保证,同一条语句两次执行可能不同。排序键有重复值又不补次级键时顺序仍不稳定,翻页会出现重复行或漏行。排序本身也不便宜:无法利用索引有序性时会落到临时文件排序。

基本信息

方向ASC 升序(默认)、DESC 降序,可逐列分别指定
多列从左到右依次比较,前一列相同才比较后一列
可用元素列名、表达式、别名、SELECT 列序号(从 1 开始)
NULL 位置由数据库与方向决定,需要固定位置时显式处理
执行位置SELECT 之后、LIMIT 之前
平台差异PostgreSQL 支持 NULLS FIRST / LAST,MySQL 用 ISNULL(col) 代替

典型使用场景

  • 分页与 Top N:不排序的 LIMIT 结果不确定,翻页会出错
  • 排行榜:按销售额、得分、时间倒序排列
  • 对账导出:固定顺序便于与下游系统逐行比对
  • 取最新一条:ORDER BY created_at DESC, id DESC LIMIT 1
  • 多级排序:先按部门再按入职时间,报表可读性更好
  • 消除顺序抖动:给排序补主键做次级键,保证结果稳定

示例

示例
SELECT id, name, created_at, amount * 1.06 AS gross
FROM orders
ORDER BY gross DESC,          # 可以用 SELECT 里定义的别名
         created_at DESC,     # 第二排序键
         id ASC               # 补主键,消除排序键重复带来的不稳定
LIMIT 20;

排序与索引

排序与索引
-- 排序键与索引列顺序一致时可直接利用索引有序性
ALTER TABLE orders ADD INDEX idx_status_created (status, created_at);
SELECT id FROM orders WHERE status = 1 ORDER BY created_at DESC LIMIT 20;
-- 该查询不再需要额外排序,EXPLAIN 的 Extra 里不会出现 Using filesort

-- 排序方向不匹配、或排序键被函数包住时会退化为文件排序
SELECT id FROM orders WHERE status = 1 ORDER BY DATE(created_at) DESC;

排序元素与限制

写法是否可用说明
列名可用最清晰,长期维护推荐
SELECT 别名可用求值顺序在 SELECT 之后
列序号 ORDER BY 2可用调整 SELECT 列顺序后含义随之改变,易错
表达式可用通常无法利用索引
聚合函数分组后可用如 ORDER BY COUNT(*) DESC
列别名不可用于 WHERE只有 ORDER BY 与 HAVING 能看到
ORDER BY 的开销取决于能否走索引:索引顺序与排序要求一致时零成本,否则需要把结果集读进内存或磁盘做排序,大结果集上磁盘排序很慢。
分页排序键不唯一时顺序不稳定:翻页可能重复或漏行,给排序补主键(或唯一键)做次级键是最简单的修法。
列序号排序可读性差且改列顺序时容易出错,长期维护的 SQL 一律用列名或别名。
PostgreSQL 里字符串排序受排序规则(collation)影响,中英文混排的结果与预期可能不同,需要固定顺序时显式指定排序规则。

排序实践要点

  • 排序键尽量与索引顺序一致:一致时可直接利用索引有序性,省掉文件排序
  • 多列排序方向一致更容易命中索引:MySQL 8.0 支持降序索引,更早版本混合方向容易退化
  • 大结果集排序先加 LIMIT:截断发生在排序之后,能明显减少中间结果
  • 分页排序键要唯一:重复值场景补主键,保证翻页不重不漏
  • 排序字段含 NULL 时显式处理位置,不同数据库默认行为不同
  • 导出与对账固定排序:附上明确的排序与唯一键,两次导出结果才能逐行比对
  • 字符串排序受排序规则影响:中英文混排与大小写敏感要在需求阶段确认

易错点

  • 排序键重复值多时不补次级键,分页可能出现重复行或漏行
  • ORDER BY 表达式通常无法走索引(除非有对应函数索引),大表排序加 LIMIT 要关注 filesort 或临时表
  • 直接用列序号排序,调整 SELECT 列顺序后排序含义静默改变
  • 依赖没有 ORDER BY 的查询顺序,升级数据库或换执行计划后结果就变了
  • 在应用层排序而不是在 SQL 里排序,分页取了错误的一页数据

数据来源与参考

  • MySQL 8.0 Reference Manual · ORDER BY
  • PostgreSQL Documentation · ORDER BY
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌