SQL LIMIT 分页查询技巧与案例

mysql 的 limit offset, count 越翻页越慢,因为需扫描 offset + count 行后丢弃前 offset 行;深分页应改用主键范围查询(如 where id > last_id order by id limit 10);不同数据库分页语法各异,且 limit 参数必须为常量整数。

SQL LIMIT 分页查询技巧与案例

MySQL 的 LIMIT offset, count 为什么越翻页越慢?

因为 MySQL 实际执行时,会先扫描 offset + count 行数据,再丢弃前 offset 行——哪怕你只要 1 条,它也可能扫了 800 万行。

  • 第 1 页(LIMIT 0, 10):扫 10 行,快
  • 第 80 万页(LIMIT 8000000, 10):扫 8000010 行,丢掉 800 万,只留 10 条
  • EXPLAIN 看不出问题,但 PROFILESending data 阶段耗时暴涨

怎么把深分页从秒级降到毫秒级?

用主键范围过滤替代 OFFSET,前提是表有单调递增的主键(如 id),且查询带 ORDER BY id

  • 先查出上一页最后一条的 id(比如是 8000001
  • 下一页直接写:SELECT * FROM users WHERE id > 8000001 ORDER BY id LIMIT 10
  • 不依赖 OFFSET,完全走主键索引,跳过所有已读数据
  • 注意:如果业务要求按时间倒序,而 create_time 有重复值,必须加 id 作为第二排序字段,否则分页结果可能错乱

不同数据库的分页语法差异在哪?

不是所有数据库都支持 LIMIT offset, count,跨库迁移或混合使用时容易出错。

  • MySQL:支持 LIMIT 10 OFFSET 20LIMIT 20, 10 两种写法
  • PostgreSQL:只认 LIMIT 10 OFFSET 20,不支持逗号语法
  • SQL Server:必须用 OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY,且 ORDER BY 不可省
  • Oracle 12c+:支持类似 SQL Server 的 FETCH,老版本得套两层子查询用 ROW_NUMBER()

LIMIT 参数不能写表达式,常见误写有哪些?

LIMIT 后的两个参数必须是常量整数,运行时不能计算,否则直接报错。

  • ❌ 错误:LIMIT (page-1)*10, 10(SQL 解析阶段就失败)
  • ❌ 错误:LIMIT @offset, @limit(除非在存储过程中预设变量,否则客户端传参需自行拼接)
  • ❌ 错误:LIMIT 10, -1(MySQL 早就不支持负数,报 ERROR 1064
  • ✅ 正确:应用层算好值再传,比如 page=3、size=10 → 拼成 LIMIT 20, 10

分页不是“写对就行”,而是“写对 + 排序稳 + 主键可推 + 参数可控”。尤其当页码超过 1000 页,OFFSET 就不再是语法问题,而是性能事故的起点。