首页/数据/SQL 速查/LIMIT 分页与 Top N

LIMIT 分页与 Top N

限定返回行数与偏移:LIMIT n 取前 n 行,OFFSET 跳过若干行;深分页性能差,生产上用键集分页替代大 OFFSET。

SQL 速查数据更新于 2026-10-13

LIMIT 是什么

LIMIT 限定返回的行数,OFFSET 指定跳过的行数,两者共同实现分页与 Top N。MySQL 支持 LIMIT n、LIMIT offset, n 与 LIMIT n OFFSET offset;PostgreSQL 用 LIMIT n OFFSET m,也支持标准的 FETCH FIRST n ROWS ONLY。LIMIT 位于执行计划的最后一步,作用在排序之后,因此它不是过滤条件:带 LIMIT 的聚合仍然按全量数据计算。深度翻页时 OFFSET 需要扫描并丢弃前面所有行,代价随页码线性增长,生产上改用键集分页。

基本信息

作用截断返回行数与跳过偏移,在排序之后执行
MySQL 语法LIMIT n / LIMIT offset, n / LIMIT n OFFSET offset
PostgreSQL 语法LIMIT n OFFSET m,标准写法 FETCH FIRST n ROWS ONLY
执行位置输出截断阶段,不是过滤条件
顺序依赖没有 ORDER BY 时取哪几行不确定
深分页代价OFFSET 越大越慢,需要扫描并丢弃前面的行

典型使用场景

  • 列表分页:每页 20 条,翻页时传偏移量
  • Top N 排行:按销售额倒序取前 10
  • 大表探查:确认表里有没有符合条件的数据,加 LIMIT 1 即可
  • 批量处理分片:按主键区间加 LIMIT 逐批拉取,避免一次拉全表
  • 导出限量:给后台导出加最大行数上限,防止拖垮实例
  • 存在性判断:LIMIT 1 比 COUNT(*) 更省资源

常用写法

常用写法
-- 分页:第 3 页每页 20 条,offset = (页码 - 1) * 每页条数
SELECT id, name FROM users ORDER BY id LIMIT 20 OFFSET 40;

-- MySQL 的另一种写法:偏移量在前,条数在后
SELECT id, name FROM users ORDER BY id LIMIT 40, 20;

-- Top N
SELECT id, total FROM orders ORDER BY total DESC LIMIT 10;

-- 存在性判断,找到一条就够
SELECT 1 FROM orders WHERE user_id = 100 LIMIT 1;

键集分页

键集分页
-- 用上一页最后一条的排序键作为下一页起点
SELECT id, created_at FROM orders
WHERE (created_at, id) < ('2024-05-01 10:00:00', 8801)
ORDER BY created_at DESC, id DESC
LIMIT 20;
# 深度翻页代价恒定,不受页码影响
# 排序键要唯一,或补主键做次级键,否则可能漏行或重复

分页方式对比

方式写法特点
LIMIT / OFFSETLIMIT 20 OFFSET 40简单直观,深翻页线性变慢
键集分页WHERE id > 上一页最大 id代价稳定,但不能跳页
游标分页服务端保存游标前台体验好,实现复杂度高
LIMIT n, mMySQL 专有,偏移量在前与标准写法顺序相反,容易写反
先查主键再回表子查询取 id 后 JOIN 原表减少回表数据量,深分页常用
LIMIT 不改变聚合结果:SELECT COUNT(*) FROM t LIMIT 10 仍返回一行总数,想限制统计范围要把条件写进 WHERE。
不加 ORDER BY 直接用 LIMIT 分页,两次请求可能返回重复或缺失的行,因为行顺序不确定。
深分页的另一种优化:先用覆盖索引取出主键,再回表取完整行,减少大量无效回表,写法是 JOIN 一个只含 id 的子查询。
PostgreSQL 还可以用 FETCH FIRST n ROWS ONLY / OFFSET m ROWS,这是 SQL 标准写法,MySQL 不支持。

分页实现要点

  • 偏移量由业务算:偏移 = (页码 - 1) × 每页条数,页码从 1 还是 0 开始要和前端约定清楚
  • 每页条数必须设上限:不限制时客户端传一个很大的 size 会一次性拉走全表
  • 总数与列表分两次查:总数走覆盖索引或缓存,列表查只取当页数据
  • 翻页必须带稳定排序键:只按非唯一列排序时补主键做次级键,否则顺序不确定
  • 键集分页不能跳页:适合无限滚动与导出分批,不适合带页码跳转的后台列表
  • 导出场景用主键区间切片:按 id 区间分批拉取,中断后可以从上次位置续传
LIMIT 与 OFFSET 的代价不对称:LIMIT 改小几乎不影响速度,OFFSET 变大才是瓶颈,因为前面的行即使不返回也要被读取并丢弃。
跨库分页优先写 LIMIT n OFFSET m,MySQL 与 PostgreSQL 都认;MySQL 专有的 LIMIT offset, n 虽然更短,但迁移时容易与标准写法混淆。
深分页的另一种常见优化是缓存总数:后台列表的总条数变化不频繁时,按条件缓存几分钟即可,不必每次请求都做一次精确统计。

易错点

  • 深分页用大 OFFSET:库要扫描并丢弃前面所有行,页码越深越慢
  • 不加 ORDER BY 就分页,结果顺序不稳定,翻页会出现重复行或漏行
  • 排序键有重复值时不补次级键,键集分页可能丢行或重复
  • 把 LIMIT 当过滤条件用于统计,实际统计仍按全量算,口径出错
  • MySQL 的 LIMIT offset, count 与标准 LIMIT count OFFSET offset 顺序相反,跨库改 SQL 时容易写反

数据来源与参考

  • MySQL 8.0 Reference Manual · SELECT · LIMIT
  • PostgreSQL Documentation · LIMIT and OFFSET
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌