跳到主要内容

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;

排序逻辑:

  1. 首先按第一个列排序
  2. 如果第一个列的值相同,则按第二个列排序
  3. 以此类推
-- 示例数据排序结果
-- | 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 FIRSTNULLS 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;

统一处理方式

使用 COALESCEISNULL 统一处理:

-- 将 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;

小结

本章我们学习了:

  1. 升序(ASC)和降序(DESC)排序
  2. 多列排序
  3. 按表达式排序
  4. 按列位置排序
  5. NULL 值排序处理
  6. 配合 LIMIT 获取 Top N
  7. 中文排序
  8. 排序性能优化

练习

  1. 查询所有用户,按注册时间降序排序
  2. 查询产品表,先按分类升序,再按价格降序排序
  3. 查询订单表,获取金额最高的 10 笔订单
  4. 查询员工表,按部门排序,同一部门按工资降序排序
  5. 查询产品表,按名称长度排序