如何从MySQL的两个关联表生成按系列分组的游戏列表
如何从MySQL的两个关联表生成按系列分组的游戏列表
看起来你已经离目标很近啦!你的需求是在网站上展示按系列分组的游戏列表,每个系列名称只在第一次出现时显示,下面依次列出对应游戏。咱们来一步步解决你遇到的问题,先分析下之前尝试的两种方式的问题,再给出可行的解决方案。
先理清楚你之前的问题
- JOIN查询的问题:确实会返回每条游戏对应一条带系列名的记录,所以前端会重复显示系列名,但这个可以通过PHP的逻辑判断来控制只在系列变化时显示名称。
- 嵌套查询的问题:你说只显示第一个系列,大概率是代码里的小错误——比如你输出系列名时用了
$row['series'],但series表的字段是series_name,不是series,这会导致输出空值,甚至可能破坏HTML结构,让后面的系列无法正常显示。
方案一:用JOIN查询+PHP逻辑控制(推荐,效率更高)
这种方式只需要一次SQL查询,然后在PHP循环里判断当前系列和上一个是否相同,相同就不显示系列名,不同才显示,完全符合你的需求。
优化后的SQL语句
SELECT s.series_name, g.game_name FROM series s JOIN games g ON s.series_name = g.series ORDER BY s.series_name, g.game_name;
这里用了表别名让语句更简洁,同时排序确保同系列的游戏排在一起,避免乱序。
对应PHP+HTML代码
<?php // 初始化变量,记录上一个显示的系列名 $last_series = ''; // 执行查询 $sql = "SELECT s.series_name, g.game_name FROM series s JOIN games g ON s.series_name = g.series ORDER BY s.series_name, g.game_name"; $result = $conn->query($sql); // 先输出表格结构 echo '<table class="s-table"> <thead> <tr> <th>Series</th> <th>Games</th> </tr> </thead> <tbody>'; if ($result->num_rows > 0) { while($row = $result->fetch_assoc()) { $current_series = $row['series_name']; $game_name = $row['game_name']; // 只有当前系列和上一个不同时,才显示系列名 $series_display = ($current_series !== $last_series) ? $current_series : ''; // 输出表格行,用htmlspecialchars防止XSS攻击 echo '<tr> <td>' . htmlspecialchars($series_display) . '</td> <td>' . htmlspecialchars($game_name) . '</td> </tr>'; // 更新上一个系列名,方便下一次判断 $last_series = $current_series; } } else { echo '<tr><td colspan="2">没有找到任何数据</td></tr>'; } echo '</tbody></table>'; ?>
方案二:修复嵌套查询的代码
如果你更倾向于用嵌套查询的方式(每个系列单独查对应游戏),那我们来修复之前的代码问题,同时优化安全性。
修复后的PHP代码
<?php // 查询所有系列,按名称排序 $sql_series = "SELECT series_name FROM series ORDER BY series_name"; $result_series = $conn->query($sql_series); // 输出表格结构 echo '<table class="s-table"> <thead> <tr> <th>Series</th> <th>Games</th> </tr> </thead> <tbody>'; if ($result_series->num_rows > 0) { while($row_series = $result_series->fetch_assoc()) { $current_series = $row_series['series_name']; $game_index = 0; // 用预处理语句查询当前系列的游戏,避免SQL注入 $sql_games = "SELECT game_name FROM games WHERE series = ? ORDER BY game_name"; $stmt = $conn->prepare($sql_games); $stmt->bind_param("s", $current_series); $stmt->execute(); $result_games = $stmt->get_result(); while($row_game = $result_games->fetch_assoc()) { // 只有该系列的第一个游戏行显示系列名 $series_display = ($game_index === 0) ? $current_series : ''; echo '<tr> <td>' . htmlspecialchars($series_display) . '</td> <td>' . htmlspecialchars($row_game['game_name']) . '</td> </tr>'; $game_index++; } // 每个系列之间加空行,和你想要的示例一致 echo '<tr><td></td><td></td></tr>'; } } else { echo '<tr><td colspan="2">没有找到任何系列</td></tr>'; } echo '</tbody></table>'; ?>
这里用了预处理语句来避免SQL注入风险,同时修复了字段名的错误,还控制了系列名只在每个系列的第一个游戏行显示,完全匹配你给出的示例效果。
小提示:添加错误排查
如果你以后遇到类似只显示部分数据的问题,可以在代码里加错误处理,快速定位问题,比如:
if (!$result_series) { die("系列查询失败: " . $conn->error); }
备注:内容来源于stack exchange,提问作者DB_Arcanum
相关产品推荐
相关产品推荐

