在mysql的查询场景中,单字段排序往往无法满足复杂的业务需求,比如先按用户等级排序,同等级的用户再按注册时间排序,这时候就需要使用ORDER BY子句实现多字段排序,理解多列的优先级规则是实现正确排序的关键。

多字段排序的基础语法
mysql中ORDER BY支持同时指定多个排序字段,基本语法格式如下:
SELECT 字段列表 FROM 表名 ORDER BY 字段1 排序规则, 字段2 排序规则, ...;
其中排序规则可选ASC(升序,默认规则,可省略)或者DESC(降序)。
多列排序的优先级规则
ORDER BY后面多个字段的优先级按照从左到右的顺序依次降低,也就是说先按照第一个字段排序,当第一个字段的值相同时,才会按照第二个字段排序,以此类推。如果第一个字段的值已经全部区分开,后续的字段排序规则不会生效。
案例演示
假设我们有一张用户表user_info,表结构如下:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | int | 用户ID |
| user_level | int | 用户等级,数值越高等级越高 |
| register_time | datetime | 注册时间 |
| score | int | 用户积分 |
表中现有测试数据:
INSERT INTO user_info (id, user_level, register_time, score) VALUES (1, 3, '2023-05-10 10:00:00', 90), (2, 2, '2023-04-01 09:00:00', 85), (3, 3, '2023-03-15 14:00:00', 95), (4, 2, '2023-06-20 11:00:00', 88), (5, 1, '2023-07-01 08:00:00', 70);
场景1:先按等级降序,同等级按注册时间升序
需求是等级高的用户排在前面,同等级的用户注册时间早的排在前面,对应的查询语句如下:
SELECT id, user_level, register_time, score FROM user_info ORDER BY user_level DESC, register_time ASC;
执行后结果顺序为:
- id为3的用户:等级3,注册时间2023-03-15,排第一(等级3且注册时间最早)
- id为1的用户:等级3,注册时间2023-05-10,排第二(同等级3,注册时间晚于id3)
- id为2的用户:等级2,注册时间2023-04-01,排第三(等级2且注册时间最早)
- id为4的用户:等级2,注册时间2023-06-20,排第四(同等级2,注册时间晚于id2)
- id为5的用户:等级1,注册时间2023-07-01,排第五(等级最低)
场景2:混合升序降序的多字段排序
如果需要先按积分降序,同积分的用户按注册时间降序,查询语句如下:
SELECT id, user_level, register_time, score FROM user_info ORDER BY score DESC, register_time DESC;
此时排序优先级依然是先按score排序,score相同才按register_time排序,当前测试数据中积分没有重复值,所以注册时间的排序规则不会生效。
常见注意事项
- 如果省略排序规则,默认所有字段都按升序排序,比如
ORDER BY user_level, register_time等价于ORDER BY user_level ASC, register_time ASC。 - 排序字段可以是表达式或者函数计算结果,比如按照用户等级和积分的总和排序:
SELECT id, user_level, score FROM user_info ORDER BY (user_level * 10 + score) DESC;
- 如果排序字段存在NULL值,mysql中NULL会被视为最小值,升序排序时NULL值会排在最前面,降序排序时NULL值排在最后面。
- 多字段排序的性能需要注意,如果排序的字段没有索引,mysql可能会使用临时表和文件排序,数据量较大时会影响查询效率,建议对常用的排序字段建立联合索引。
总结
mysql的多字段排序通过ORDER BY子句实现,多列的优先级严格按照从左到右的顺序生效,前面的字段值相同才会触发后续字段的排序规则。实际使用中可以根据业务需求灵活组合升序和降序规则,同时注意排序字段的索引情况,避免大数据量下的性能问题。