SQL 排序与分页有哪几种写法?三种方式一次讲清

编程狮(w3cschool.cn) 2026-10-12 07:04:59 浏览数 (71)
反馈

你写列表接口时,十有八九要排序和分页。核心答案一句话:排序用 ORDER BY 指定字段和方向,分页用 LIMIT 加偏移量取一小段;数据量大、排序分页变慢时,再靠索引和覆盖扫描兜底。但 ORDER BY 和 LIMIT 各自都有坑:排序可能触发 filesort 把内存吃满,分页在大偏移量时会越翻越慢,两件事叠在一起更容易让接口超时。本文以 MySQL 8.0 为例,先分别讲清排序与分页的几种写法,再讲它们叠加时的性能陷阱和优化手段,顺带把游标分页、组合索引这些实用方案也说透。看完你不仅能写出正确的列表查询,还能判断什么时候该加索引、什么时候该换分页策略。今天这篇文章,编程狮就把这块讲透。

SQL 排序与分页三种写法一次讲清

先看结论

排序与分页不是两个孤立动作,要先排后取,叠加时性能陷阱最多:

你的场景 首选写法 一句话理由
小数据量、简单列表 ORDER BY + LIMIT 语法简单,加好索引就够快
大偏移量深翻页 游标分页或延迟关联 避免越翻越慢的偏移扫描
排序字段无索引或高频变更 组合索引覆盖 把 filesort 和回表一起干掉

一、SQL 排序与分页要解决的两个问题

精确查询 WHERE id = 1 只要定位一行,但列表页的本质是「先看一部分、再按顺序看」。排序解决的是「按什么顺序看」,分页解决的是「一次看多少、从第几条开始看」。两者叠加才是后台列表接口的真正形态,也是 SQL 排序与分页这道基本功的全部内容。

精确等于查不出「前 20 条最新的订单」或「按价格从高到低展示」。这类需求只能交给排序与分页共同完成。要系统学语法,可以先过一遍 SQL 教程,把 SELECT 的基本结构铺平。

⚠️ 注意:排序与分页都依赖「有序」这个前提。如果排序列上没有索引,数据库就得先把结果集全部读出来再排序,数据一大就会非常吃力,这一点在后面的性能陷阱里会重点讲。

二、ORDER BY 排序:方向、多列与性能

ORDER BY 的语法非常直白:跟在字段名后面用 ASC 表示升序,DESC 表示降序;省略时默认升序。最常见的是按时间倒序看最新数据。

-- 按创建时间倒序,最新的 20 条在最前面
SELECT id, title, created_at
FROM articles
ORDER BY created_at DESC
LIMIT 20;

上面这条做了「先排序、再取前 20 条」。预期结果里第一条是创建时间最大的记录。需要多列排序时,依次列出字段即可,例如先按状态升序、再按时间倒序。

-- 状态在前、时间在后,两列共同决定顺序
SELECT id, status, created_at
FROM orders
ORDER BY status ASC, created_at DESC
LIMIT 20;

性能的关键在排序列有没有索引。有索引时,MySQL 可以顺着 B+ 树的顺序直接取,几乎不额外消耗;没有索引时,会走 filesort,把参与排序的行读进排序缓冲区,内存不够还要落盘,速度断崖式下跌。不同数据库的语法细节可以翻 MySQL 速查手册 查具体用法。

SQL 排序与分页写法怎么选?

三、LIMIT 分页:偏移量与深翻页陷阱

分页用 LIMIT 实现,它接受两个参数:第一个是「跳过的条数」(偏移量),第二个是「要取的条数」。第一页偏移 0,第二页偏移 20,依此类推。

-- 第 1 页:取前 20 条
SELECT id, title FROM articles ORDER BY id DESC LIMIT 0, 20;

-- 第 3 页:跳过前 40 条,取接下来的 20 条
SELECT id, title FROM articles ORDER BY id DESC LIMIT 40, 20;

写法很简单,但 LIMIT 的偏移量越大越慢,这是分页查询最经典的坑。原因是 LIMIT 40000, 20 并不是「直接跳到第 40001 条」,而是数据库要先读出 40020 条,再丢掉前 40000 条,只返回最后 20 条。偏移量到几十万时,这种浪费会让接口明显变慢。

-- 深翻页:偏移量越大越慢
SELECT id, title FROM articles ORDER BY id DESC LIMIT 40000, 20;

解决深翻页有两个实用思路:一是游标分页,让前端把上一页最后一条的 id 传回来,用 WHERE id < ? 代替大偏移量;二是延迟关联,先只查索引拿 id,再回表取详情,减少回表行数。

深翻页 OFFSET 与游标分页 SQL 对比示例

四、SQL 排序分页叠加时的性能陷阱

把排序查询和分页查询放在一起,问题会成倍放大。最典型的场景是「按非索引字段排序后再深翻页」:数据库先 filesort 排好序,再跳过几万行取一小段,CPU 和 IO 一起爆。本质上,排序和分页都依赖「顺序读取」,而大偏移量偏偏要打断这种顺序。

方案 适用场景 优点 代价
ORDER BY + LIMIT 小数据量、浅翻页 语法最简单 深翻页越翻越慢
游标分页(WHERE id < ?) 无限下拉、深翻页 不随偏移量变慢 不支持随机跳页
组合索引覆盖 排序分页高频且稳定 干掉 filesort 和回表 索引要针对查询设计

踩坑清单

  1. 现象:列表接口翻到后面越来越卡;原因:LIMIT 偏移量过大导致先读后丢;修复:改用游标分页或延迟关联。
  2. 现象:排序字段加了索引还是慢;原因:索引顺序与 ORDER BY 不匹配,仍走 filesort;修复:建「WHERE 等值 + ORDER BY 字段」的组合索引。
  3. 现象:排序结果不稳定、同分记录乱跳;原因:排序列不唯一,每次顺序不确定;修复:追加一个唯一字段(如主键 id)作为兜底排序。
  4. 现象:排序直接拖垮内存;原因:filesort 缓冲区不够,落到磁盘临时文件;修复:缩小排序行宽或增大排序缓冲区并加索引。

总结

SQL 排序与分页的写法本身很简单:排序靠 ORDER BY 指定字段和方向,分页靠 LIMIT 加偏移量。真正的难点在两者叠加时的性能——小数据量、浅翻页直接写就够,深翻页要换游标分页或延迟关联,高频排序分页则用组合索引把 filesort 和回表一起干掉。记住最痛的一课:LIMIT 的大偏移量不是「跳转」而是「先读后丢」,生产环境一定要规避。

要点带走:

  • ORDER BY 决定顺序,LIMIT 决定取哪一段,二者必须配合;
  • 排序列没索引会走 filesort,数据一大就吃力;
  • 深翻页用游标分页或延迟关联,别硬扛大偏移量。

下一步可以动手给常用列表字段补上组合索引,直观感受排序分页耗时的变化。

动手验证清单

下面这组动作帮你在本机验证三种排序分页写法,按你自己的环境执行,结果以实际输出为准(本文 SQL 未在作者本机执行,仅给出预期形态):

  1. 安装:本地起一个 MySQL 8.0,或用 Docker 拉取镜像;
  2. 配置:建一张 articles 表,插入几万条带 created_at 的测试数据;
  3. 命令:执行 ORDER BY created_at DESC LIMIT 0, 20 验证浅翻页;
  4. 运行:在命令行或客户端跑该语句,观察返回的前 20 行;
  5. 检查:用 EXPLAIN 查看是否出现 Using filesort;
  6. 预期:无索引时 Extra 显示 filesort,加索引后消失;
  7. 失败:若深翻页 LIMIT 40000, 20 明显变慢;
  8. 修复:改用 WHERE id < ? 的游标分页重写;
  9. 验证:对比两种写法的执行耗时,确认游标分页更稳。

延伸学习

想把 SQL 检索能力补完整,可以顺着这条线:

  1. 先过一遍 SQL 入门课程,把基础语法铺平;
  2. 对比性能看 MySQL 代价估计器怎么用;
  3. 想优化复杂查询再读 SQL 分层查询怎么写。

常见问题

Q:ORDER BY 和 LIMIT 一起用,顺序有讲究吗?

A:必须先 ORDER BY 后 LIMIT,语义是「先排好序,再取前 N 条」。如果反过来或漏掉排序,取出来的那一段顺序是不确定的,分页结果会错乱。

Q:为什么 LIMIT 偏移量越大查询越慢?

A:LIMIT 40, 20 的真实动作是先把前 60 条读出来再丢掉前 40 条,只返回后 20 条。偏移量越大,被读出来又扔掉的行越多,CPU 和 IO 浪费越严重,所以会越翻越慢。

Q:深翻页除了游标分页还有别的办法吗?

A:有。延迟关联先通过覆盖索引只拿主键 id,再按 id 回表取详情,能显著减少回表行数;另外也可以限制最大翻页深度,超过一定页数就提示用户缩小筛选条件。

0 人点赞