許多教程使用 id+1/id-1 實現上下篇,但文章 ID 常因刪除而不連續。正確做法:查詢比當前 ID 小的最大 ID(上一篇)與比當前 ID 大的最小 ID(下一篇)。
-- 上一篇:ID < 當前,按 ID 降序取第一條 SELECT * FROM article WHERE id < $current_id ORDER BY id DESC LIMIT 1; -- 下一篇:ID > 當前,按 ID 升序取第一條 SELECT * FROM article WHERE id > $current_id ORDER BY id ASC LIMIT 1;
..., 'title'=>..., 'url'=>...]
*/
function getAdjacentArticle($type, $table, $catid = 0, $id) {
$conn = getDbConnection(); // 你的資料庫連接函數
$where = "id " . ($type === 'pre' ? '<' : '>') . " " . (int)$id;
if ($catid) $where .= " AND catid = " . (int)$catid;
$order = $type === 'pre' ? 'DESC' : 'ASC';
$sql = "SELECT id, title FROM {$table} WHERE {$where} ORDER BY id {$order} LIMIT 1";
$result = $conn->query($sql);
if (!$result || $result->num_rows === 0) return false;
$row = $result->fetch_assoc();
return [
'id' => $row['id'],
'title' => $row['title'],
'url' => "article.php?id={$row['id']}" . ($catid ? "&catid={$catid}" : '')
];
}
// 使用範例
$currentId = (int)($_GET['id'] ?? 0);
$catId = (int)($_GET['catid'] ?? 0);
$prev = getAdjacentArticle('pre', 'article', $catId, $currentId);
$next = getAdjacentArticle('next', 'article', $catId, $currentId);
// 前端輸出
echo '';
if ($prev) {
echo '← 上一篇:' . $prev['title'] . '';
} else {
echo '← 上一篇:已是第一篇';
}
if ($next) {
echo '下一篇:' . $next['title'] . ' →';
} else {
echo '下一篇:已是最新篇 →';
}
echo '';
利用 UNION 減少資料庫往返:
function getPrevNextSingleQuery($table, $catid, $id) {
$conn = getDbConnection();
$catSql = $catid ? "AND catid = " . (int)$catid : "";
$sql = "
(SELECT 'pre' AS type, id, title FROM {$table} WHERE id < {$id} {$catSql} ORDER BY id DESC LIMIT 1)
UNION
(SELECT 'next' AS type, id, title FROM {$table} WHERE id > {$id} {$catSql} ORDER BY id ASC LIMIT 1)
";
$result = $conn->query($sql);
$data = ['pre' => false, 'next' => false];
while ($row = $result->fetch_assoc()) {
$data[$row['type']] = [
'id' => $row['id'],
'title' => $row['title'],
'url' => "article.php?id={$row['id']}" . ($catid ? "&catid={$catid}" : '')
];
}
return $data;
}
文章內容頁訪問頻率高,上下篇關係相對固定,適合緩存:
function getCachedAdjacent($table, $catid, $id) {
$cacheKey = "article_nav_{$table}_{$catid}_{$id}";
$cached = apcu_fetch($cacheKey); // 或 Redis/Memcached
if ($cached) return $cached;
$data = getPrevNextSingleQuery($table, $catid, $id);
apcu_store($cacheKey, $data, 3600); // 緩存 1 小時
return $data;
}
// 文章更新/刪除時清理相關緩存
function clearArticleNavCache($table, $catid, $id) {
// 清理當前文章及鄰近文章的緩存
$ids = [$id];
// 可選:查詢實際鄰近 ID 並一併清理
foreach ($ids as $targetId) {
apcu_delete("article_nav_{$table}_{$catid}_{$targetId}");
}
}
| 錯誤做法 | 問題 | 正確做法 |
|---|---|---|
id-1 / id+1 | ID 不連續導致 404 或錯誤文章 | 鄰近查詢 ORDER BY id LIMIT 1 |
| 未加分類過濾 | 跨分類跳轉,用戶體驗斷層 | 加 catid 條件或提供「全站/分類」切換 |
| 每次請求都查庫 | 高併發下資料庫壓力大 | APCu/Redis 緩存,更新時失效 |
| 標題過長撐破佈局 | 移動端顯示異常 | CSS text-overflow: ellipsis 截斷 |
WHERE id < ? ORDER BY id DESC LIMIT 1(上一篇)UNION 獲取雙向導航,效能最優正確實現上下篇導航,不僅解決 ID 不連續問題,更能通過緩存優化顯著降低資料庫負載,是內容型網站必備的基礎功能優化點。