
本文介绍如何通过一条 sql 查询高效获取所有餐厅及其对应的最新评论(若存在),避免重复行问题,适用于 php 主页列表渲染场景。
本文介绍如何通过一条 sql 查询高效获取所有餐厅及其对应的最新评论(若存在),避免重复行问题,适用于 php 主页列表渲染场景。
在构建餐厅展示页时,常需同时列出所有餐厅信息(如名称、菜系、地址)及每家餐厅的最新一条用户评论(含标题、内容、发布时间等)。若采用多查询或嵌套循环,不仅性能低下,还增加 PHP 层逻辑复杂度;而简单 LEFT JOIN reviews 又会导致一个餐厅匹配多条评论,产生重复餐厅记录。
理想的解决方案是:用单条 SQL 完成“每个餐厅最多关联一条最新评论”的关联。核心思路是——先聚合出每个餐厅的最新 posted_at 时间,再通过该时间精准回连到具体评论记录。
以下为推荐的高性能单查询写法:
SELECT r.id AS restaurant_id, r.name, r.cuisine, r.address, r.image_file, rev.id AS review_id, rev.header, rev.content, rev.posted_at, rev.modified_at, u.username AS reviewer_name, u.email AS reviewer_email FROM restaurants r LEFT JOIN ( SELECT restaurant_id AS restId, MAX(posted_at) AS lastTime FROM reviews GROUP BY restaurant_id ) AS last_reviews ON last_reviews.restId = r.id LEFT JOIN reviews rev ON rev.restaurant_id = r.id AND rev.posted_at = last_reviews.lastTime LEFT JOIN users u ON u.id = rev.user_id;
✅ 关键设计说明:
- 第一层
LEFT JOIN子查询(last_reviews)按restaurant_id分组求最大posted_at,确保每个餐厅至多一行聚合结果; - 第二层
LEFT JOIN reviews使用 复合条件(restaurant_id + posted_at)精确命中唯一评论——即使存在同秒多评(极罕见),此方案仍可能返回多行;如需绝对唯一(例如取id最大的那条),可改用窗口函数(MySQL 8.0+):
-- MySQL 8.0+ 更稳健方案(按 posted_at 降序 + id 降序取首行)
LEFT JOIN (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY restaurant_id ORDER BY posted_at DESC, id DESC) AS rn
FROM reviews
) rev ON rev.restaurant_id = r.id AND rev.rn = 1
⚠️ 注意事项:
- 确保
reviews(restaurant_id, posted_at)上建立联合索引,大幅提升子查询与回连性能:CREATE INDEX idx_rev_rest_time ON reviews(restaurant_id, posted_at);
- 若某餐厅无评论,
rev.*和u.*字段将为NULL,PHP 中需做空值判断(如if ($row['review_id']) { ... }); - 避免使用
SELECT *生产环境,应显式列出所需字段,提升可读性与传输效率。
最终在 PHP 中只需一次查询 + 单层 foreach 即可完成渲染:
$stmt = $pdo->query($sql);
foreach ($stmt as $row) {
echo "<div class="restaurant">";
echo "<h3>{$row['name']}</h3>";
echo "<p>{$row['cuisine']} · {$row['address']}</p>";
if ($row['review_id']) {
echo "<blockquote>
<strong>{$row['header']}</strong><br>{$row['content']}<br>";
echo "<small>— {$row['reviewer_name']} @ " . date('Y-m-d', strtotime($row['posted_at'])) . "</small>
</blockquote>";
}
echo "</div>";
}
该方案兼顾简洁性、可维护性与数据库性能,是关系型数据库中“Top-N per Group”问题的经典实践。











