ORDER BY 排序
ORDER BY 子句用于对查询结果进行排序。本章将详细介绍排序的各种用法。
基本排序
升序排序
使用 ASC 关键字(默认)进行升序排序:
-- 按年龄升序排序(ASC 可省略)
SELECT * FROM users ORDER BY age ASC;
SELECT * FROM users ORDER BY age; -- 等价于上面
降序排序
使用 DESC 关键字进行降序排序:
-- 按年龄降序排序
SELECT * FROM users ORDER BY age DESC;
-- 按价格降序排序
SELECT * FROM products ORDER BY price DESC;
多列排序
可以按多个列进行排序,优先级从左到右:
-- 先按城市升序,再按年龄降序
SELECT * FROM users
ORDER BY city ASC, age DESC;
-- 先按部门排序,再按工资降序,最后按入职日期升序
SELECT * FROM employees
ORDER BY department, salary DESC, hire_date;
排序逻辑:
- 首先按第一个列排序
- 如果第一个列的值相同,则按第二个列排序
- 以此类推
-- 示例数据排序结果
-- | name | city | age |
-- |------|-------|-----|
-- | 张三 | 北京 | 25 |
-- | 李四 | 北京 | 20 | <- 同城按年龄降序
-- | 王五 | 上海 | 30 |
-- | 赵六 | 上海 | 28 |
按表达式排序
可以按计算结果排序:
-- 按总价排序(单价 × 数量)
SELECT *, price * quantity AS total
FROM order_items
ORDER BY price * quantity DESC;
-- 按字符串长度排序
SELECT * FROM products
ORDER BY LENGTH(name);
-- 按日期的年份排序
SELECT * FROM orders
ORDER BY YEAR(order_date);
按列位置排序
可以使用列的位置编号代替列名:
-- 按第二列排序
SELECT name, age FROM users ORDER BY 2;
-- 等价于
SELECT name, age FROM users ORDER BY age;
-- 多列排序
SELECT name, city, age FROM users ORDER BY 2, 3 DESC;
提示
按位置排序在 UNION 查询中特别有用,因为 UNION 后的列名可能不确定。
NULL 值排序
不同数据库对 NULL 的排序处理不同:
MySQL
NULL 值在升序时排在前面,降序时排在后面:
-- NULL 排在最前面
SELECT * FROM users ORDER BY phone ASC;
-- NULL 排在最后面
SELECT * FROM users ORDER BY phone DESC;
PostgreSQL
使用 NULLS FIRST 或 NULLS LAST 控制:
-- NULL 排在最前面
SELECT * FROM users ORDER BY phone NULLS FIRST;
-- NULL 排在最后面
SELECT * FROM users ORDER BY phone NULLS LAST;
-- 升序但 NULL 在最后
SELECT * FROM users ORDER BY phone ASC NULLS LAST;
SQL Server
NULL 值视为最小值:
-- NULL 排在最前面
SELECT * FROM users ORDER BY phone ASC;
统一处理方式
使用 COALESCE 或 ISNULL 统一处理:
-- 将 NULL 替换为默认值后再排序
SELECT * FROM users
ORDER BY COALESCE(phone, 'zzz') ASC; -- NULL 排在最后
SELECT * FROM users
ORDER BY ISNULL(phone, 'zzz') ASC; -- SQL Server
配合 LIMIT 使用
排序常与 LIMIT 配合获取 Top N:
-- 获取价格最高的 10 个产品
SELECT * FROM products
ORDER BY price DESC
LIMIT 10;
-- 获取最新 5 条订单
SELECT * FROM orders
ORDER BY created_at DESC
LIMIT 5;
-- 获取销量前 3 的产品
SELECT product_id, COUNT(*) as sales_count
FROM order_items
GROUP BY product_id
ORDER BY sales_count DESC
LIMIT 3;
中文排序
中文排序需要考虑数据库的字符集设置:
MySQL
-- 按拼音排序
SELECT * FROM users ORDER BY name;
-- 按拼音排序(指定字符集)
SELECT * FROM users ORDER BY CONVERT(name USING gbk);
-- 按笔画排序
SELECT * FROM users ORDER BY name COLLATE utf8mb4_zh_0900_as_cs;
PostgreSQL
-- 按拼音排序
SELECT * FROM users ORDER BY name COLLATE "zh_CN.utf8";
性能优化
使用索引
ORDER BY 可以利用索引提高性能:
-- 如果在 name 列上有索引
CREATE INDEX idx_name ON users(name);
-- 这个查询可以使用索引
SELECT * FROM users ORDER BY name;
-- 这个查询无法使用索引(排序方向不一致)
SELECT * FROM users ORDER BY name DESC;
避免排序大量数据
-- 先过滤再排序,减少排序数据量
SELECT * FROM orders
WHERE status = 'completed'
ORDER BY created_at DESC
LIMIT 100;
-- 而不是
SELECT * FROM orders
ORDER BY created_at DESC
LIMIT 100;
使用覆盖索引
-- 创建覆盖索引
CREATE INDEX idx_status_created ON orders(status, created_at);
-- 查询可以完全使用索引
SELECT id, status, created_at
FROM orders
WHERE status = 'completed'
ORDER BY created_at DESC;
小结
本章我们学习了:
- 升序(ASC)和降序(DESC)排序
- 多列排序
- 按表达式排序
- 按列位置排序
- NULL 值排序处理
- 配合 LIMIT 获取 Top N
- 中文排序
- 排序性能优化
练习
- 查询所有用户,按注册时间降序排序
- 查询产品表,先按分类升序,再按价格降序排序
- 查询订单表,获取金额最高的 10 笔订单
- 查询员工表,按部门排序,同一部门按工资降序排序
- 查询产品表,按名称长度排序