CREATE TABLE bbsrow (
bbsrow_id INT(6) NOT NULL AUTO_INCREMENT PRIMARY KEY,
bbsrow_auth VARCHAR(20) NOT NULL,
bbsrow_parentid INT(6) DEFAULT NULL, -- 父文章 ID,NULL 為主題文
bbsrow_title VARCHAR(200) NOT NULL,
bbsrow_returncount INT(3) DEFAULT NULL, -- 回覆數統計
bbsrow_content TEXT, -- 文章內容
bbsrow_posttime DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
function showChildren($parent_id, $depth = 0) {
global $conn;
$sql = "SELECT * FROM bbsrow WHERE bbsrow_parentid = " . intval($parent_id) . " ORDER BY bbsrow_posttime";
$result = mysql_query($sql, $conn);
if (mysql_num_rows($result) == 0) return;
echo "";
while ($row = mysql_fetch_assoc($result)) {
echo "- ";
// 縮排視覺化
echo str_repeat(" ", $depth);
echo "{$row['bbsrow_title']} by {$row['bbsrow_auth']} ";
echo "({$row['bbsrow_posttime']})
";
echo nl2br(htmlspecialchars($row['bbsrow_content']));
// 遞迴顯示子回覆
if ($row['bbsrow_returncount'] > 0) {
showChildren($row['bbsrow_id'], $depth + 1);
}
echo " ";
}
echo "
";
}
<?php
$conn = mysql_connect("localhost", "user", "pass") or die("連線失敗");
mysql_select_db("forum", $conn) or die("選庫失敗");
mysql_query("SET NAMES utf8");
// 1. 取得所有主題文 (親文章)
$sql = "SELECT * FROM bbsrow WHERE bbsrow_parentid IS NULL ORDER BY bbsrow_posttime DESC";
$mainResult = mysql_query($sql, $conn);
// 2. 逐筆顯示主題文及其回覆樹
while ($main = mysql_fetch_assoc($mainResult)) {
echo "";
echo "{$main['bbsrow_title']}
";
echo "作者: {$main['bbsrow_auth']} | 時間: {$main['bbsrow_posttime']}
";
echo "" . nl2br(htmlspecialchars($main['bbsrow_content'])) . "";
// 顯示回覆樹
if ($main['bbsrow_returncount'] > 0) {
showChildren($main['bbsrow_id']);
}
echo "
";
}
// 3. 分頁邏輯 (簡易版)
$perPage = 10;
$page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
$offset = ($page - 1) * $perPage;
// ... 修改主題文查詢加 LIMIT $offset, $perPage
// ... 計算總頁數輸出分頁連結
?>
| 問題 | 解決方案 |
|---|---|
| 遞迴查詢 N+1 問題 (每層一個 SELECT) | 一次查詢所有資料,PHP 端建立樹狀陣列 (父ID 分組),單次遍歷輸出 |
| 深層回覆遞迴深度過大導致堆疊溢位 | 改用迭代 + 陣列堆疊模擬遞迴,或限制最大回覆層數 (如 10 層) |
| bbsrow_returncount 需手動維護 | 使用 Trigger 或應用層寫入時同步更新 |
| 舊版 mysql_* 函數已棄用 | 改用 mysqli 或 PDO 預備陳述式 |
$sql = "SELECT * FROM bbsrow WHERE bbsrow_parentid IS NULL ORDER BY bbsrow_posttime DESC LIMIT ?, ?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("ii", $offset, $perPage);
$stmt->execute();
$threads = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
// 一次撈取所有回覆
$allReplies = $mysqli->query("SELECT * FROM bbsrow WHERE bbsrow_parentid IS NOT NULL ORDER BY bbsrow_parentid, bbsrow_posttime")
->fetch_all(MYSQLI_ASSOC);
// PHP 端建樹
$children = [];
foreach ($allReplies as $r) $children[$r['bbsrow_parentid']][] = $r;
function renderTree($parentId, $depth, $children) {
if (!isset($children[$parentId])) return;
echo "";
foreach ($children[$parentId] as $r) {
echo "- " . str_repeat("→", $depth) . htmlspecialchars($r['bbsrow_title']) . "...";
renderTree($r['bbsrow_id'], $depth+1, $children);
echo "
";
}
echo "
";
}
foreach ($threads as $t) {
echo "" . htmlspecialchars($t['bbsrow_title']) . "
";
renderTree($t['bbsrow_id'], 0, $children);
}