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 时显式处理位置,不同数据库默认行为不同
- 导出与对账固定排序:附上明确的排序与唯一键,两次导出结果才能逐行比对
- 字符串排序受排序规则影响:中英文混排与大小写敏感要在需求阶段确认