如何通过单条SQL查询获取多个指定分类下各1条最新新闻?
可以通过单条查询实现该需求,以下是具体实现方案:
方案1:窗口函数实现(推荐,适配MySQL 8.0+、MariaDB 10.2+及以上主流环境)
步骤1:给分类表加标记字段
你可以先在categories表新增is_show字段,灵活控制需要展示的子分类,后续调整展示分类不需要改代码:
ALTER TABLE categories ADD COLUMN `is_show` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否在新闻区块展示:0否1是'; -- 给四个需要展示的子分类设置标记,后面的seo值请替换为你实际的分类seo链接 UPDATE categories SET is_show = 1 WHERE cat_seo_url IN ('travel','news','art','business');
步骤2:单条查询语句
SELECT t.* FROM ( SELECT p.post_catId, p.post_seo_url, p.post_desc, p.post_type, p.post_status, p.post_title, p.post_image_url, c.catId, c.catName, c.cat_seo_url, -- 按分类分区,同分类内按发布时间倒序排名 ROW_NUMBER() OVER (PARTITION BY p.post_catId ORDER BY p.post_created_at DESC) AS rn FROM posts p LEFT JOIN categories c ON p.post_catId = c.catId WHERE c.is_show = 1 AND p.post_status = ? ) t WHERE t.rn = 1 -- 取每个分类排名第一的最新新闻
方案2:关联子查询实现(适配MySQL 5.7及以下老版本环境)
如果你的数据库不支持窗口函数,可以用这个替代方案:
SELECT p.post_catId, p.post_seo_url, p.post_desc, p.post_type, p.post_status, p.post_title, p.post_image_url, c.catId, c.catName, c.cat_seo_url FROM posts p LEFT JOIN categories c ON p.post_catId = c.catId WHERE c.is_show = 1 AND p.post_status = ? AND p.post_created_at = ( SELECT MAX(post_created_at) FROM posts WHERE post_catId = p.post_catId AND post_status = ? ) GROUP BY p.post_catId
PHP调用示例
和你原有写法完全兼容:
// 窗口函数版本参数绑定 $stmt = $pdo->prepare($sql); // 如果是老版本关联子查询方案,这里要传两个参数:$stmt->execute([1,1]); $stmt->execute([1]); $newsList = []; if($stmt->rowCount() > 0){ while($row = $stmt->fetch()){ // 按分类seo url存储方便前端调用,也可以直接遍历输出 $newsList[$row['cat_seo_url']] = $row; } } // 后续直接取对应分类的新闻即可,比如艺术分类是$newsList['art']
优化建议
- 给
posts表加联合索引idx_cat_status_time(post_catId, post_status, post_created_at),可以大幅提升查询效率 - 如果后续需要调整展示的分类,只需要修改
categories表的is_show字段值即可,不需要修改业务代码
内容的提问来源于stack exchange,提问作者user1805543
相关产品推荐
相关产品推荐

