PHP/SQL关联两张表并获取课程平均评分的实现问题
问题:展示2017年课程及对应平均评分时重复显示相同评分
我来帮你解决这个问题!你需要列出2017年开设的所有课程,并在每门课旁显示对应的平均反馈评分,但当前代码里每一行都重复显示相同的评分,咱们一步步来排查修复:
先明确你的数据表结构
courses表
| course_id | course_name |
|---|---|
| 1 | Public speaking |
| 2 | Social media skills |
feedback表
| course_id | overall_rating |
|---|---|
| 1 | 3 |
| 1 | 5 |
| 1 | 4 |
| 1 | 4 |
| 2 | 3 |
| 2 | 3 |
| 2 | 4 |
问题根源分析
你的代码有两个核心问题:
- SQL硬编码错误:获取平均评分的语句
SELECT AVG(overall_rating) FROM feedback WHERE courseid='courseid'里,'courseid'是字符串字面量,不是循环中的变量,所以每次查询都在找courseid等于字符串"courseid"的记录(显然不存在或只会返回固定ID的评分),导致所有行显示相同结果。 - 低效的数据库交互:在循环中每次单独查询一次平均评分,会增加数据库的负载,性能较差。
解决方案
方案1:修复原循环逻辑(基础修复)
把SQL中的courseid替换为当前循环的课程ID,同时用参数绑定避免SQL注入:
<table cellspacing="0" border="1" cellpadding="5"> <tr style="font-size:20px; font-weight:bold; text-align:center;"> <td colspan="3">2017</td> </tr> <tr style="font-weight:bold;"> <td style="background-color:#AED6F1;">Course ID</td> <td style="background-color:#AED6F1;">Course title</td> <td style="background-color:#AED6F1;">Average rating</td> </tr> <?php // 获取2017年所有课程 $yearscourses = "SELECT courseid, coursetitle FROM courses WHERE coursedate1 BETWEEN '2017-01-01' AND '2017-12-31'"; $yearsresult = mysqli_query($connect, $yearscourses); if (mysqli_num_rows($yearsresult) > 0) { while ($row = mysqli_fetch_array($yearsresult, MYSQLI_ASSOC)) { $courseId = $row['courseid']; // 使用参数绑定查询当前课程的平均评分,避免SQL注入 $avgrating = "SELECT AVG(overall_rating) AS avg_rating FROM feedback WHERE courseid = ?"; $stmt = mysqli_prepare($connect, $avgrating); mysqli_stmt_bind_param($stmt, "i", $courseId); mysqli_stmt_execute($stmt); $avgresult = mysqli_stmt_get_result($stmt); $row2 = mysqli_fetch_assoc($avgresult); // 处理无反馈的情况,显示N/A或0 $avgRating = $row2['avg_rating'] ?? "N/A"; // 格式化评分,保留1位小数 $avgRating = is_numeric($avgRating) ? number_format($avgRating, 1) : $avgRating; echo "<tr> <td>" . htmlspecialchars($row['courseid']) . "</td> <td>" . htmlspecialchars($row['coursetitle']) . "</td> <td>" . $avgRating . "</td> </tr>\n"; mysqli_stmt_close($stmt); } } else { echo "<tr><td colspan='3'>No courses found in 2017</td></tr>"; } ?> </table>
方案2:使用单SQL查询(推荐,更高效)
通过LEFT JOIN关联两张表,一次性获取所有课程和对应的平均评分,减少数据库交互:
<table cellspacing="0" border="1" cellpadding="5"> <tr style="font-size:20px; font-weight:bold; text-align:center;"> <td colspan="3">2017</td> </tr> <tr style="font-weight:bold;"> <td style="background-color:#AED6F1;">Course ID</td> <td style="background-color:#AED6F1;">Course title</td> <td style="background-color:#AED6F1;">Average rating</td> </tr> <?php // 一次性查询2017年课程及对应平均评分 $query = " SELECT c.courseid, c.coursetitle, AVG(f.overall_rating) AS avg_rating FROM courses c LEFT JOIN feedback f ON c.courseid = f.courseid WHERE c.coursedate1 BETWEEN '2017-01-01' AND '2017-12-31' GROUP BY c.courseid, c.coursetitle "; $result = mysqli_query($connect, $query); if (mysqli_num_rows($result) > 0) { while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) { $avgRating = $row['avg_rating'] ?? "N/A"; $avgRating = is_numeric($avgRating) ? number_format($avgRating, 1) : $avgRating; echo "<tr> <td>" . htmlspecialchars($row['courseid']) . "</td> <td>" . htmlspecialchars($row['coursetitle']) . "</td> <td>" . $avgRating . "</td> </tr>\n"; } } else { echo "<tr><td colspan='3'>No courses found in 2017</td></tr>"; } ?> </table>
额外提示
- 用
htmlspecialchars()转义输出内容,防止XSS攻击。 LEFT JOIN确保即使课程没有任何反馈,也会被显示出来;如果用INNER JOIN则只会显示有反馈的课程。
内容的提问来源于stack exchange,提问作者Chris Hawkins
相关产品推荐
相关产品推荐

