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),都会让索引失效。
