
本文介绍如何通过 sql 查询准确获取指定 id 记录的前驱(prev_id)和后继(next_id),适用于 id 不连续、无序或存在删除缺口的场景,并提供安全、可集成的 php 实现方案。
在实际开发中,分页导航、内容翻页(如“上一篇/下一篇”)常需基于主键 ID 获取逻辑上的相邻记录。但当表中 ID 并非严格递增连续(例如因软删除、批量导入或手动分配导致 ID 为 22 → 50 → 81),直接使用 id-1 或 id+1 将失效。此时,应依赖有序比较而非数值差值。
正确的 SQL 思路是:
- 前一条记录的 ID = 当前 ID 之前所有 ID 的最大值 → SELECT MAX(id) FROM table WHERE id
- 后一条记录的 ID = 当前 ID 之后所有 ID 的最小值 → SELECT MIN(id) FROM table WHERE id > ?
以下是一个健壮、防 SQL 注入的 PHP 方法实现(以 PDO 为例):
public function prevNext(int $id): array
{
$sql = "SELECT
(SELECT MAX(id) FROM `table` WHERE id < ?) AS prev_id,
(SELECT MIN(id) FROM `table` WHERE id > ?) AS next_id";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([$id, $id]);
return $stmt->fetch(PDO::FETCH_ASSOC) ?: ['prev_id' => null, 'next_id' => null];
}✅ 关键优势说明:
- 使用子查询 + 聚合函数(MAX/MIN),语义清晰且性能良好(配合 id 字段上的索引可达到 O(log n));
- 两个子查询在同一主查询中执行,仅需一次数据库往返;
- 显式绑定参数(?),彻底规避 SQL 注入风险;
- 返回 null 表示无前驱/后继(如当前为第一条或最后一条记录),便于前端判断。
⚠️ 注意事项:
- 确保 id 字段已建立索引(通常是主键,自动索引);若非主键,请手动添加 INDEX(id) 提升查询效率;
- 避免在 WHERE 子句中对 id 使用函数(如 ABS(id - ?)),否则将导致索引失效;
- 若业务逻辑依赖其他排序字段(如 created_at),则需改用 ORDER BY created_at LIMIT 1 配合 OFFSET,但本方案专注 ID 顺序场景。
综上,该方法简洁、可靠、符合关系型数据库设计原则,是处理非连续主键翻页需求的标准实践。










