分页查询是 PHP 后台和内容列表里非常常见的功能。文章列表、商品列表、订单记录、用户管理、日志查询,只要数据量变大,就不能一次性全部查出来,而应该按页加载。
PHP 配合 MySQL 实现分页,核心是根据当前页和每页数量计算偏移量,再使用 LIMIT 查询当前页数据,同时查询总条数计算总页数。本文从基础公式讲起,再整理参数校验、SQL 写法和前端展示思路。
分页核心参数
分页通常需要三个核心参数:当前页 page、每页数量 pageSize、总条数 total。有了这些,就能计算总页数和偏移量。
$page = 1;
$pageSize = 10;
$offset = ($page - 1) * $pageSize;
第一页偏移量是 0,第二页偏移量是 10,第三页偏移量是 20。这个公式是分页查询的基础。
接收页码参数
页码通常来自 GET 参数。读取后要转换为整数,并限制最小值。
$page = max(1, (int)($_GET['page'] ?? 1));
$pageSize = 10;
$offset = ($page - 1) * $pageSize;
不要直接把用户传入的 page 拼进 SQL。即使是数字,也应该转换和限制,避免异常参数影响查询。

限制每页数量
如果允许用户指定每页数量,一定要设置上限。否则有人传一个很大的 pageSize,可能导致数据库压力过大。
$pageSize = (int)($_GET['pageSize'] ?? 10);
$pageSize = min(max($pageSize, 1), 100);
这表示每页数量最小 1,最大 100。后台管理系统和公开接口都应该做这种限制。
查询总条数
分页通常需要先查询符合条件的总条数。
$stmt = $pdo->prepare('SELECT COUNT(*) FROM articles WHERE status = ?');
$stmt->execute(['published']);
$total = (int)$stmt->fetchColumn();
总条数用于计算总页数,也能让前端展示“共多少条数据”。如果查询条件很多,总数查询要和列表查询保持一致。
计算总页数
总页数可以使用 ceil() 向上取整。
$totalPages = (int)ceil($total / $pageSize);
如果总条数为 0,总页数可以返回 0,也可以按业务返回 1。前后端要统一约定。
LIMIT 查询
查询当前页数据时使用 LIMIT 和偏移量。
$stmt = $pdo->prepare(
'SELECT id, title, created_at FROM articles WHERE status = ? ORDER BY id DESC LIMIT ? OFFSET ?'
);
$stmt->bindValue(1, 'published', PDO::PARAM_STR);
$stmt->bindValue(2, $pageSize, PDO::PARAM_INT);
$stmt->bindValue(3, $offset, PDO::PARAM_INT);
$stmt->execute();
$items = $stmt->fetchAll();
绑定 LIMIT 和 OFFSET 时建议使用整数类型。不同数据库和 PDO 配置下,直接 execute 数组传参可能会把数字当字符串处理。
返回 JSON 结构
接口返回分页数据时,建议把列表和分页信息分开。
echo json_encode([
'items' => $items,
'pagination' => [
'page' => $page,
'pageSize' => $pageSize,
'total' => $total,
'totalPages' => $totalPages
]
], JSON_UNESCAPED_UNICODE);
结构稳定后,前端分页组件就能复用,不必每个接口单独适配。
前端页码展示
前端展示分页时,通常需要当前页、总页数、上一页、下一页和页码列表。当前页为 1 时禁用上一页,当前页等于总页数时禁用下一页。
const canPrev = page > 1;
const canNext = page < totalPages;
页码很多时,不建议一次显示所有页码,可以显示当前页附近几页,再用省略号表示中间部分。
搜索条件分页
分页经常和搜索条件一起使用。点击下一页时,要保留原来的搜索关键词、分类、状态等参数。
/articles.php?keyword=php&status=published&page=2
后端查询总数和列表时,都要使用同样的筛选条件,否则前端会看到页数和实际数据对不上。
深分页问题
当 page 很大时,LIMIT offset, size 会越来越慢,因为数据库需要跳过大量记录。比如偏移 100000 行,性能可能明显下降。
对于数据量很大的场景,可以考虑游标分页,也就是根据上一页最后一条记录的 ID 或时间继续查询。
SELECT * FROM articles WHERE id < ? ORDER BY id DESC LIMIT 10
游标分页不适合跳到任意页,但适合信息流、日志列表、加载更多等场景。
排序稳定性
分页查询一定要有稳定排序。不要在没有 ORDER BY 的情况下分页,否则不同请求返回顺序可能不一致。
ORDER BY created_at DESC, id DESC
如果创建时间可能重复,可以加上 ID 作为第二排序字段,让顺序更稳定。
常见错误
第一种错误是 page 不做最小值限制,导致 offset 为负数。第二种错误是 pageSize 不设上限。第三种错误是总数查询和列表查询条件不一致。第四种错误是分页没有稳定排序。第五种错误是深分页性能问题被忽略。
实践建议
PHP 分页查询可以按这个流程实现:读取并校验 page 和 pageSize,计算 offset,查询 total,计算 totalPages,使用 LIMIT 查询当前页,返回 items 和 pagination。数据量很大时,再考虑游标分页优化。
分页看似简单,但它关系到性能、接口结构和前端体验。把参数校验、总数计算、排序和返回结构做好,分页功能才会稳定。













暂无评论内容