排序与分页 ORDER BY 与 LIMIT
本教程共 46 篇 · 第 22 篇 · 更新于 2026-07-30 · 约 10 分钟阅读
22. 排序与分页 ORDER BY 与 LIMIT
本节目标:学会用 ORDER BY 控制查询结果的排序顺序(升序、降序、多列排序),用 LIMIT 实现分页查询,理解深分页的性能问题及优化思路。
22.1 ORDER BY:排序
查询出来的数据默认是无序的。如果你希望按某个列排序,用 ORDER BY:
SELECT username, age FROM users
ORDER BY age;
默认是升序(从小到大),等价于显式写 ASC:
SELECT username, age FROM users
ORDER BY age ASC;
想从大到小排,用 DESC(降序):
SELECT username, age FROM users
ORDER BY age DESC;
Note不加 ORDER BY 的查询结果不保证顺序。你可能发现多次查询结果”碰巧”顺序一样,但那只是巧合,MySQL 没有任何排序保证。需要有序结果时,必须写 ORDER BY。
22.2 多列排序
按多个列排序时,用逗号分隔,排序有优先级:
SELECT username, age, created_at FROM users
ORDER BY age DESC, created_at ASC;
先按 age 降序排,age 相同的再按 created_at 升序排。
每个列可以单独指定升序或降序:
-- 年龄从大到小,同龄的按注册时间从早到晚
SELECT username, age, created_at FROM users
ORDER BY age DESC, created_at ASC;
Tip排序列的顺序很重要。
ORDER BY age DESC, created_at ASC和ORDER BY created_at ASC, age DESC结果完全不同。排在前面的列优先级更高,先按它排,排不出来(值相同)的才看后面的列。
22.3 按别名和表达式排序
ORDER BY 在 SELECT 之后执行(参考第 19 章的执行顺序),所以可以用列别名和表达式:
-- 用别名排序
SELECT username, age * 365 AS 出生天数
FROM users
ORDER BY 出生天数 DESC;
-- 用表达式排序
SELECT username, age
FROM users
ORDER BY age * -1;
-- 等价于 ORDER BY age DESC,但可读性差
也可以用列的序号位置排序:
-- 按第二列排序
SELECT username, age FROM users
ORDER BY 2;
-- 等价于 ORDER BY age
Warning用列序号排序(
ORDER BY 2)不推荐。一旦改了 SELECT 的列顺序,排序就错了。而且可读性差,谁知道 2 是哪个列?老老实实写列名或别名。
22.4 NULL 在排序中的位置
MySQL 默认把 NULL 排在最前面(升序时)或最后面(降序时):
-- 升序:NULL 排最前
SELECT username, age FROM users ORDER BY age ASC;
-- NULL, 20, 25, 30, ...
-- 降序:NULL 排最后
SELECT username, age FROM users ORDER BY age DESC;
-- 30, 25, 20, ..., NULL
Note这跟其他数据库不一样。Oracle 默认升序时 NULL 排最后,PostgreSQL 也是。如果你从别的数据库迁移过来,注意这个差异。MySQL 8.0.13+ 支持原生
NULLS FIRST/NULLS LAST语法控制 NULL 位置。26.7 基线下可直接使用。如果要在更早版本(8.0.13 之前)实现相同效果,需要通过 CASE 表达式实现跨数据库兼容。
22.5 LIMIT:限制返回行数
查出来太多行,只想要前几条?用 LIMIT:
-- 只返回前 5 条
SELECT * FROM users
ORDER BY age DESC
LIMIT 5;
LIMIT 通常跟 ORDER BY 配合使用。不排序的话,LIMIT 返回的是”随便”的 5 条,没有实际意义。
LIMIT 偏移分页
分页查询是 LIMIT 最常见的用法。语法是 LIMIT 偏移量, 行数:
-- 第 1 页(偏移 0,取 10 条)
SELECT * FROM users
ORDER BY id ASC
LIMIT 0, 10;
-- 第 2 页(偏移 10,取 10 条)
SELECT * FROM users
ORDER BY id ASC
LIMIT 10, 10;
-- 第 3 页(偏移 20,取 10 条)
SELECT * FROM users
ORDER BY id ASC
LIMIT 20, 10;
偏移量的计算公式:偏移量 = (页码 - 1) * 每页行数。
还有一种写法用 OFFSET 关键字,效果一样但可读性更好:
SELECT * FROM users
ORDER BY id ASC
LIMIT 10 OFFSET 20;
先写行数再写偏移量,跟 LIMIT 20, 10 是同一个意思。
Tip
LIMIT offset, count这种写法容易搞混哪个是偏移哪个是数量。推荐用LIMIT count OFFSET offset写法,语义更清晰。
22.6 深分页性能问题
分页查到后面几页时,性能会急剧下降。这叫”深分页”问题。
-- 第 10000 页,每页 10 条
SELECT * FROM orders
ORDER BY id ASC
LIMIT 99990, 10;
这条语句虽然只返回 10 条数据,但 MySQL 实际上要先查出前 99990 条,然后丢掉它们,只返回最后 10 条。数据量越大,翻得越深,越慢。
优化方案一:延迟关联(子查询优化)
利用覆盖索引先查出 id,再关联回原表:
SELECT o.*
FROM orders o
INNER JOIN (
SELECT id FROM orders
ORDER BY id ASC
LIMIT 99990, 10
) AS t ON o.id = t.id;
子查询 SELECT id 只查索引列(如果 id 是主键,走覆盖索引),不回表,速度快。拿到 10 个 id 后再关联回去取完整数据。
优化方案二:记住上一页的最后一条
不使用偏移量,而是用上一页最后一条的 id 作为起点:
-- 假设上一页最后一条 id 是 99990
SELECT * FROM orders
WHERE id > 99990
ORDER BY id ASC
LIMIT 10;
这种方式完全没有偏移开销,无论翻到第几页都一样快。缺点是只能”上一页/下一页”翻,不能直接跳到第 N 页。
优化方案三:限制最大翻页数
产品层面控制,比如最多允许翻 100 页。大多数用户根本不会翻到第 1000 页,深分页很多时候是爬虫在搞事。
Warning深分页不是 SQL 写法的问题,而是 LIMIT OFFSET 机制本身的局限。理解原理后选合适的优化方案。我之前优化过一个报表系统,把 LIMIT 偏移改成 WHERE id > 上次最大id,查询时间从 3 秒降到 2 毫秒。
22.7 LIMIT 在 UPDATE 和 DELETE 中的妙用
LIMIT 不只能用在 SELECT 里。前面章节提到过,它可以用在 UPDATE 和 DELETE 中限制操作行数:
-- 只更新最早的 100 条 pending 订单
UPDATE orders
SET status = 'expired'
WHERE status = 'pending'
ORDER BY created_at ASC
LIMIT 100;
-- 只删最早的 1000 条过期订单
DELETE FROM orders
WHERE status = 'expired'
ORDER BY created_at ASC
LIMIT 1000;
分批操作大表数据时,这个用法非常实用。
22.8 排序与索引的关系
如果 ORDER BY 的列上有索引,MySQL 可能直接利用索引的有序性,跳过排序步骤(叫”using index”或”filesort”优化)。
-- 如果 age 上有索引,排序可以直接走索引
SELECT * FROM users ORDER BY age;
-- 如果是复合索引 (status, created_at),这个排序也能走索引
SELECT * FROM orders WHERE status = 'paid'
ORDER BY created_at;
但有些情况索引帮不了:
ORDER BY和WHERE用了不同的索引列- 升降序混合且索引不是按这个方向建的
- 排序列上有函数或表达式
NoteMySQL 用
filesort(文件排序)来处理无法走索引的排序。数据量大时 filesort 会写临时文件,消耗磁盘 IO。用EXPLAIN看到 Extra 列有Using filesort就要注意了,索引优化章节会详细讲。
22.9 小结
ORDER BY 控制排序,LIMIT 控制行数,两者配合实现分页。核心记住:排序要写列名不用序号、分页推荐 LIMIT count OFFSET offset 写法、深分页用 WHERE id > 上次最大id 优化、UPDATE/DELETE 也能配合 LIMIT 分批操作。下一章学聚合函数和分组统计。