mysql分页核心是用limit offset,length动态计算偏移量,php需强转并校验$page为正整数,用独立count(*)查总数且条件与主查询一致,前端页码用ceil()计算总页数并兜底处理0记录情况。

MySQL LIMIT 子句怎么配合 PHP 实现分页
PHP 分页本质是靠 SQL 的 LIMIT 控制每次查多少条、从哪条开始。关键不是“PHP 怎么分页”,而是“怎么让 SELECT 每次只取一页数据”。LIMIT offset, length 中的 offset 必须动态计算:比如每页 10 条,第 3 页的 offset 就是 (3 - 1) * 10 = 20。
常见错误是直接用 $_GET['page'] 算偏移量却不校验:
- 没过滤非数字或负数,导致 SQL 报错或越权读取
- 没处理
page = 0或超总页数的情况,LIMIT -10, 10会报语法错误 - 没对
offset做max(0, ...)保护,用户传?page=-5就崩了
PHP 中如何安全计算分页参数
别手写公式拼接,用整型强转 + 边界控制最稳妥。假设每页显示 $per_page = 10:
$page = (int)($_GET['page'] ?? 1); $page = max(1, $page); // 至少为 1 $offset = ($page - 1) * $per_page;
注意两点:
立即学习“PHP免费学习笔记(深入)”;
-
(int)强转比intval()更严格,"1abc"会变1,"abc"直接变0,后续max(1, 0)就兜住了 - 总记录数必须单独查一次(用
COUNT(*)),不能靠mysqli_num_rows()查完数据再算——那已经把全部结果拉下来了,失去分页意义 - 如果用 PDO,预处理时
$offset和$per_page必须用PDO::PARAM_INT绑定,不能当字符串塞进去
为什么 COUNT(*) 查询不能和主查询合并
有人想用子查询或变量一次性拿到总数和数据,比如:SELECT *, (SELECT COUNT(*) FROM table) AS total FROM table LIMIT ?,?。这看似省一次查询,但实际更慢:
- MySQL 无法优化这种写法,
COUNT(*)会全表扫描,哪怕你只要前 10 行 - 如果表有上百万行,每次翻页都触发全表 COUNT,响应直接卡住
- 正确做法是独立执行一条
SELECT COUNT(*) FROM table WHERE ...(条件必须和主查完全一致),用缓存或近似值(如SHOW TABLE STATUS)进一步优化
前端分页链接怎么避免暴露逻辑漏洞
生成页码 HTML 时,别直接把 $page 塞进 URL,例如 <a href="?page=<?=%24i?>">=$i?></a>。用户手动改 URL 仍可跳转,但问题在后端没做校验时就危险了。
更关键的是总页数计算方式:
$total = $pdo->query("SELECT COUNT(*) FROM users")->fetchColumn();
$total_pages = (int)ceil($total / $per_page);这里 ceil() 必须用,否则 25 / 10 = 2.5 取整成 2,第 3 页就没了。另外,如果 $total 是 0,$total_pages 应为 1 而不是 0,否则页码导航可能不显示任何链接。
复杂点在于:真实业务中 WHERE 条件常带用户输入(搜索关键词、状态筛选),这些必须同步用于 COUNT 和主查询,漏一个字段,总数和数据就对不上——这是线上最常被忽略的细节。











