文章上一篇/下一篇功能的本質是:在同一分類下,找到當前文章 ID 之前/之後最近的記錄。關鍵在於 SQL 查詢的寫法直接決定性能。
-- 上一篇:ID 最大且小於當前 ID
SELECT article_id, title
FROM article
WHERE article_id = (
SELECT MAX(article_id)
FROM article
WHERE article_id < $current_id AND cat_id = $cat_id
);
-- 下一篇:ID 最小且大於當前 ID
SELECT article_id, title
FROM article
WHERE article_id = (
SELECT MIN(article_id)
FROM article
WHERE article_id > $current_id AND cat_id = $cat_id
);
優點:邏輯清晰,利用索引效率高。缺點:需執行兩次查詢。
-- 上一篇 SELECT article_id, title FROM article WHERE article_id < $current_id AND cat_id = $cat_id ORDER BY article_id DESC LIMIT 1; -- 下一篇 SELECT article_id, title FROM article WHERE article_id > $current_id AND cat_id = $cat_id ORDER BY article_id ASC LIMIT 1;
注意:若未建立 (cat_id, article_id) 聯合索引,大數據量下 ORDER BY 會觸發 filesort,性能急劇下降。
(
SELECT article_id, title, 'prev' AS type
FROM article
WHERE article_id < $current_id AND cat_id = $cat_id
ORDER BY article_id DESC LIMIT 1
)
UNION ALL
(
SELECT article_id, title, 'current' AS type
FROM article
WHERE article_id = $current_id
)
UNION ALL
(
SELECT article_id, title, 'next' AS type
FROM article
WHERE article_id > $current_id AND cat_id = $cat_id
ORDER BY article_id ASC LIMIT 1
);
優勢:單次查詢獲取三條記錄,減少數據庫往返。結果集按 type 字段區分,應用層解析即可。
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
// 下一篇
$query = mysql_query("SELECT id,title FROM article WHERE id > '$id' ORDER BY id ASC LIMIT 1");
$next = mysql_fetch_array($query);
// 上一篇
$query = mysql_query("SELECT id,title FROM article WHERE id < '$id' ORDER BY id DESC LIMIT 1");
$prev = mysql_fetch_array($query);
class ArticleNavigator {
private $pdo;
private $table = 'article';
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function getAdjacent($id, $cat_id) {
$sql = "
(SELECT id, title, 'prev' AS rel FROM {$this->table} WHERE id < ? AND cat_id = ? ORDER BY id DESC LIMIT 1)
UNION ALL
(SELECT id, title, 'current' AS rel FROM {$this->table} WHERE id = ?)
UNION ALL
(SELECT id, title, 'next' AS rel FROM {$this->table} WHERE id > ? AND cat_id = ? ORDER BY id ASC LIMIT 1)
";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([$id, $cat_id, $id, $id, $cat_id]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
$result = ['prev' => null, 'current' => null, 'next' => null];
foreach ($rows as $row) {
$result[$row['rel']] = ['id' => $row['id'], 'title' => $row['title']];
}
return $result;
}
}
// 使用
$nav = new ArticleNavigator($pdo);
$adjacent = $nav->getAdjacent(100, 5);
echo "上一篇: {$adjacent['prev']['title']}";
echo "下一篇: {$adjacent['next']['title']}";
必須建立聯合索引:
ALTER TABLE `article` ADD INDEX `idx_cat_id` (`cat_id`, `article_id`);
此索引可同時支援 WHERE 篩選和 ORDER BY 排序,避免臨時表和文件排序。
選擇 UNION ALL 方案配合正確索引,可在千萬級數據量下保持毫秒級響應,是高性能文章導航的最佳實踐。