如何用mysql实现分页查询功能_mysql项目常见需求

来源:这里教程网 时间:2026-02-28 20:38:12 作者:

MySQL 分页用
LIMIT
OFFSET
最直接

绝大多数分页场景,靠

LIMIT
+
OFFSET
就能解决。它语法简单,语义清晰:跳过前
OFFSET
行,取接下来
LIMIT
行。

比如查第 3 页、每页 10 条:

SELECT * FROM orders ORDER BY created_at DESC LIMIT 10 OFFSET 20;

注意:这里的

OFFSET 20
表示跳过前 20 行(即前 2 页),不是“从第 20 行开始”。容易误写成
OFFSET 30
或漏掉
ORDER BY
—— 没有明确排序时,分页结果不可预测,同一页可能反复出现/丢失数据。

OFFSET
值 =
(page_no - 1) * page_size
,别手算错
必须配合
ORDER BY
,且排序字段最好有索引(如
created_at
或主键)
OFFSET
很大(比如 > 10 万),性能会明显下降——MySQL 仍需扫描并丢弃前面所有行

大数据量下用游标分页(
WHERE id > ? LIMIT ?
)更高效

当总记录数超百万、用户频繁翻到几十页以后,

OFFSET
方式变慢甚至超时。此时应改用基于上一页最后 ID 的游标分页(也叫 keyset 分页)。

假设你刚查完第 2 页,最后一条记录的

id
12345
,那么第 3 页就该这样查:

SELECT * FROM orders WHERE id > 12345 ORDER BY id ASC LIMIT 10;

关键点:

必须用有序、唯一、有索引的字段(推荐主键
id
或带时间戳的组合字段)
方向要一致:
ORDER BY id ASC
对应
WHERE id > ?
;若用
DESC
,就得写
WHERE id 
不能跳页(比如从第 1 页直接跳到第 100 页),但对“下一页”“上一页”体验更好、更稳定 前端需把上一页末尾的
id
值传回后端,而不是传页码

避免
SQL_CALC_FOUND_ROWS
获取总条数

老项目里常见这种写法:

SELECT SQL_CALC_FOUND_ROWS * FROM users WHERE status = 1 LIMIT 10 OFFSET 20;
SELECT FOUND_ROWS();

看起来省一次查询,实际代价很高:

SQL_CALC_FOUND_ROWS
会让 MySQL 扫描全表或全索引匹配行数,即使你只取 10 条。在高并发或大数据量下,这步常成瓶颈。

更务实的做法:

前端不强求显示“共 N 条”,只提供“下一页”按钮(游标分页天然适合) 如果真要总数,单独走一个轻量
COUNT(*)
查询,但加缓存(比如 Redis 存 5 分钟内的统计值)
对模糊搜索类分页(如
LIKE '%xxx%'
),
COUNT
本身就很慢,不如前端限制最大可翻页数(如最多到第 100 页)

ORM 框架里的分页陷阱(以 Python SQLAlchemy 和 Node.js Knex 为例)

框架封装了分页逻辑,但底层仍是

LIMIT/OFFSET
或手动拼游标条件,容易忽略细节。

SQLAlchemy 中常见错误写法:

query.offset(20).limit(10)  # 没有 .order_by(),结果不稳定

Knex 中易错点:

knex('posts').limit(10).offset(20)  // 同样缺 order by

另外,某些 ORM 的

paginate()
方法默认不带
COUNT
,但文档没说清,导致前端以为有总页数,实际是估算或未实现——务必查清你用的版本是否真执行了
COUNT
查询。

真正要注意的,是排序字段有没有索引、偏移量是否动态计算正确、以及游标值有没有被前端篡改(比如传个负数或非数字 ID)。这些地方一松懈,分页就乱序、重复或漏数据。

相关推荐