mysql 8.0+ 使用 limit n offset m 实现分页,需配合确定性 order by(如主键),其中 offset = (页码−1)×每页条数;但深度分页时因全扫描前m行导致性能下降,建议用游标分页优化。

MySQL 8.0+ 怎么用 OFFSET 和 LIMIT 做分页?
OFFSET 和 LIMIT 是最直接的分页方式,但容易在数据量大时拖慢查询。比如查第 100 页、每页 20 条:SELECT * FROM users ORDER BY id LIMIT 20 OFFSET 2000。这里 OFFSET 2000 会让 MySQL 先跳过前 2000 行,再取 20 行——实际扫描了 2020 行,不是只查 20 行。
-
LIMIT必须配合ORDER BY使用,否则结果顺序不稳定 -
OFFSET越大,性能越差,尤其当表有百万级数据且没有合适索引时 - 如果
id是主键且连续,可以用WHERE id > last_seen_id替代OFFSET(游标分页) -
LIMIT 20 OFFSET 0和LIMIT 20等价,但显式写OFFSET 0有助于逻辑统一
PostgreSQL 分页为什么推荐用 FETCH FIRST?
PostgreSQL 从 8.4 开始支持 LIMIT/OFFSET,但从 8.5 起更推荐用标准 SQL 的 FETCH FIRST 语法,语义更清晰且兼容性更好:SELECT * FROM orders ORDER BY created_at DESC FETCH FIRST 10 ROWS ONLY OFFSET 30。
-
FETCH FIRST n ROWS ONLY和LIMIT n功能一致,但前者是 SQL 标准语法 -
OFFSET行为和 MySQL 完全相同,同样存在深分页性能问题 - PostgreSQL 对
ORDER BY + LIMIT的索引优化比 MySQL 更激进,但依然依赖索引覆盖排序字段 - 如果排序字段有重复值(如多个记录
created_at相同),必须加唯一字段(如id)做二级排序,否则分页结果可能错位
SQL Server 分页怎么避开 TOP + NOT IN 的坑?
早期常用 TOP 配合 NOT IN 实现分页,比如:SELECT TOP 20 * FROM users WHERE id NOT IN (SELECT TOP 80 id FROM users ORDER BY id)。这种写法在 id 有空值或重复时会漏数据,且子查询无法利用索引下推。
- 正确做法是用
OFFSET-FETCH(SQL Server 2012+):SELECT * FROM users ORDER BY id OFFSET 80 ROWS FETCH NEXT 20 ROWS ONLY -
OFFSET-FETCH要求必须带ORDER BY,否则报错:Msg 102, Level 15, State 1, Line X: Incorrect syntax near 'OFFSET' - 如果要兼容老版本 SQL Server(2005–2008),可用
ROW_NUMBER()包裹查询,但需注意ORDER BY必须唯一,否则ROW_NUMBER()分配可能不稳定 -
FETCH NEXT 20 ROWS ONLY中的NEXT不可省略,也不能写成ROWS或ROW
分页时 ORDER BY 字段没索引会怎样?
没索引的 ORDER BY 字段会让分页查询变成全表扫描+文件排序(Using filesort),哪怕只取 10 行,也可能耗时数秒。
- 检查执行计划:MySQL 看
EXPLAIN输出中type是否为index或range,Extra是否含Using filesort - 复合索引要注意顺序:如果分页语句是
ORDER BY status, created_at,索引应建为(status, created_at),而不是反过来 - 如果排序字段是表达式(如
ORDER BY UPPER(name)),普通索引无效,需建函数索引(MySQL 8.0+/PostgreSQL 支持) - 游标分页(基于上一页最后一条记录的排序字段值)能彻底绕过
OFFSET,但要求前端保存并传回该值,且后端必须校验其有效性
分页看着简单,真正上线后出问题的,八成卡在排序字段没索引、OFFSET 越来越大、或者跨页数据变动导致重复/丢失——这些都不是语法写对就能躲开的。











