ORDER BY 是 MySQL 中唯一可靠的排序方式,不显式声明则结果顺序不可预测;必须置于 WHERE/GROUP BY/HAVING 之后、LIMIT 之前,多字段排序按书写顺序逐级生效,无索引时触发 filesort 导致性能下降。

ORDER BY 是排序的唯一标准方式
MySQL 中没有“默认排序”这回事,不写 ORDER BY 就意味着结果顺序不可靠——哪怕看起来每次一样,也可能在优化器升级、索引重建或并发查询后突然变化。必须显式声明排序逻辑。
基本用法很简单:SELECT * FROM users ORDER BY created_at DESC。但要注意几个关键点:
-
ORDER BY必须放在WHERE、GROUP BY、HAVING之后,LIMIT之前 - 多个字段排序时,用逗号分隔:
ORDER BY status ASC, updated_at DESC,前一个字段相等时才比较后一个 - NULL 值默认排在最前(ASC)或最后(DESC),可用
IS NULL或IS NOT NULL显式控制位置,比如:ORDER BY (score IS NULL) ASC, score DESC
ORDER BY 字段没索引会明显拖慢查询
如果 ORDER BY 的字段没走索引,MySQL 得把所有匹配行读出来再做文件排序(Using filesort),数据量一大就卡。执行 EXPLAIN SELECT ... 看 Extra 列是否含 Using filesort 是最直接判断方式。
常见应对策略:
- 对常用排序字段单独建索引,比如
CREATE INDEX idx_created_at ON orders(created_at) - 复合索引要匹配
ORDER BY的字段顺序和方向:若常查WHERE category = ? ORDER BY price ASC, id DESC,索引应为(category, price, id);注意 ASC/DESC 混用时,MySQL 8.0+ 才支持多方向索引,旧版本只对首个 DESC 有效 - 避免在排序字段上用函数或表达式,如
ORDER BY UPPER(name)无法利用索引,改用生成列 + 索引更稳妥
中文排序需额外处理 collation
默认 utf8mb4_general_ci 或 utf8mb4_0900_as_cs 这类 collation 对中文是按 Unicode 码点排的,不是拼音或笔画顺序,结果常不符合预期(比如“张”排在“李”前面)。
解决办法取决于需求场景:
- 按拼音排序:用
utf8mb4_unicode_ci(MySQL 8.0+ 推荐)或utf8mb4_pinyin_ci(某些定制版支持),或显式转换:ORDER BY CONVERT(name USING gbk) COLLATE gbk_chinese_ci - 精确控制顺序:加拼音辅助列,如
name_pinyin VARCHAR(100),插入/更新时用应用层或触发器填充,然后对该列建索引并ORDER BY name_pinyin - 临时排序需求:用
ORDER BY FIELD(id, 3,1,4)可按指定 ID 顺序返回,但仅适合小集合硬编码,不能替代常规排序
LIMIT 配合 ORDER BY 才真正安全
单独用 LIMIT 不带 ORDER BY 是危险的,尤其分页时:SELECT * FROM logs LIMIT 10 OFFSET 100 结果不稳定。OFFSET 越大越慢,因为 MySQL 仍需扫描前 110 行。
更可靠的分页方案:
- 用游标分页(cursor-based):记录上一页最后一条的
created_at和id,下一页查WHERE created_at - 避免大 OFFSET:当必须用 offset 分页时,先用子查询定位主键再关联,例如:
SELECT * FROM users JOIN (SELECT id FROM users ORDER BY id LIMIT 1000, 20) AS t USING (id) -
ORDER BY字段必须有高选择性且稳定(比如时间戳+主键组合),否则可能漏行或重复
排序不是加个 ORDER BY 就完事的事,它直连索引设计、字符集配置和分页逻辑。最容易被忽略的是:没索引的排序在测试库看不出问题,一到生产环境数据量上来,慢查询日志里全是它。


















