MySQL的ORDER BY支持列名、列序号、表达式或别名;ASC/DESC按字段独立生效,默认升序;NULL默认排最前;带LIMIT必须加ORDER BY保证顺序;性能取决于是否命中索引。

ORDER BY 后面能跟哪些东西
MySQL 的 ORDER BY 子句支持三种常见排序依据:列名、列序号(位置编号)、表达式或别名。但要注意,列序号只在 SELECT 列表中存在且为常量/字段时才安全可用——比如 SELECT name, age ORDER BY 2 表示按 age 排,但如果 SELECT 中有函数如 UPPER(name),用位置编号就容易出错,尤其后续改写 SELECT 时容易断掉。
别名可以用于 ORDER BY,但必须是 SELECT 中定义的列别名(AS 后的名字),不能是表别名或未出现在 SELECT 中的字段别名。例如:
SELECT id, CONCAT(first_name, ' ', last_name) AS full_name FROM users ORDER BY full_name;
这个没问题;但下面这句会报错:
SELECT id FROM users ORDER BY full_name; -- full_name 没在 SELECT 列表里,也不在 FROM 中,直接报 Unknown column
ASC 和 DESC 的作用范围和默认行为
ASC 和 DESC 是针对每个排序字段独立生效的,不是全局修饰符。也就是说,ORDER BY a ASC, b DESC 表示先按 a 升序,相同 a 值时再按 b 降序。很多人误以为写了 DESC 就整条语句都降序,其实不会。
-
ORDER BY col等价于ORDER BY col ASC,不写明就是升序 -
NULL值在升序中排最前(MySQL 8.0+ 默认行为,可被SET SESSION sort_buffer_size或 SQL mode 影响) - 如果想让
NULL排最后,得显式写成ORDER BY col IS NULL, col ASC
带 LIMIT 时 ORDER BY 为什么不能省略
当查询带 LIMIT 但没写 ORDER BY,MySQL 返回的数据顺序是不确定的——可能每次执行结果都不同,尤其是表有并发写入、使用 InnoDB 且数据页分裂后。这不是 bug,而是标准行为:SQL 标准规定,不声明排序就没有“保证顺序”这一说。
常见踩坑场景:
- 分页查询写成
SELECT * FROM log LIMIT 10, 20,没加ORDER BY id,第二页可能漏掉或重复某条记录 - 取最新一条记录写成
SELECT * FROM events LIMIT 1,实际返回的可能是任意一条,不是时间最大的那条
正确做法始终是:只要对“第几条”有业务含义,就必须搭配确定性排序字段(最好是主键或带索引的时间字段)。
ORDER BY 性能差?先看有没有走索引
ORDER BY 是否快,关键看是否能利用索引避免文件排序(Using filesort)。执行 EXPLAIN 查看 Extra 字段:
- 出现
Using filesort:说明 MySQL 要额外做一次排序操作,数据量大时很慢 - 没出现该提示,且
key显示用了某个索引:大概率走的是索引有序扫描,性能好
索引能否生效,取决于 ORDER BY 字段是否是索引的最左前缀,且没有被 WHERE 中的范围条件(如 >、BETWEEN)截断。例如:
CREATE INDEX idx_status_ctime ON orders(status, create_time);
下面这句能用上索引:
SELECT * FROM orders WHERE status = 'paid' ORDER BY create_time DESC;
但换成:
SELECT * FROM orders WHERE status > 'canceled' ORDER BY create_time DESC;
就无法利用 create_time 部分排序,因为 status > 是范围查询,索引的后续字段失效。
多字段排序对索引要求更严格,也更容易忽略隐式类型转换导致索引失效——比如 ORDER BY phone + 0 或 ORDER BY CAST(phone AS UNSIGNED),都会让索引失效。










