当前位置: 首页
数据库
SQL查询每个分类下最新更新的一条完整记录

SQL查询每个分类下最新更新的一条完整记录

热心网友 时间:2026-06-24
转载

使用窗口函数`ROW_NUMBER()`按分类和更新时间倒序编号并取首行,可获取每个分类的最新完整记录。注意附加`id`排序避免同秒结果不稳定。MySQL老版本可用子查询配合`MAX(updated_at)`自连接,但需警惕同时间多条记录;PostgreSQL的`DISTINCTON`更简洁。过滤条件应置于子查询内,避免逻辑偏差。

先聊一个不少同学踩过的坑:用 GROUP BYMAX(updated_at) 确实能拿到每个分类的最新更新时间戳,但你要的是那条完整记录——时间戳之外的字段全没着落。真正靠谱的解法是窗口函数 ROW_NUMBER() 按分类和更新时间倒序编号,然后只取编号为 1 的行。MySQL 里可以配合 JOIN 子查询,PostgreSQL 则有更简洁的 DISTINCT ON。另外提醒一句:过滤条件放错位置,结果会悄悄出错。

如何在SQL中统计每个分类下最新更新的一条完整记录?

用窗口函数 ROW_NUMBER() 按分类和时间排序取首行

假设表名叫 articles,字段有 categorytitlecontentupdated_at。标准写法长这样:

SELECT category, title, content, updated_at
FROM (
  SELECT *,
         ROW_NUMBER() OVER (
           PARTITION BY category 
           ORDER BY updated_at DESC, id DESC
         ) AS rn
  FROM articles
) ranked
WHERE rn = 1;

这里有两个容易忽略的细节。一是 ORDER BY updated_at DESC 确保最新记录排在最前;二是额外加上 id DESC——如果同一分类下多条记录在同一秒更新,不指定 id 会导致每次查询结果不稳定,加个 id 排序就能避免这种“随机性”。

MySQL 5.7 或更老版本不支持窗口函数怎么办

老版本没有窗口函数,得用关联子查询或自连接,性能差一些,写法也绕。核心思路是:先对每个分类求出最大 updated_at,再连回原表匹配。推荐用 INNER JOIN + 子查询,相对易读:

SELECT a1.*
FROM articles a1
INNER JOIN (
  SELECT category, MAX(updated_at) AS max_updated
  FROM articles
  GROUP BY category
) a2 ON a1.category = a2.category AND a1.updated_at = a2.max_updated;

但有个陷阱:如果同一分类下有多条记录的 updated_at 完全相同,这个写法会返回多条——你拿到的不是“最新一条”,而是“最新时间点的所有记录”。要想严格只取一条,可以在子查询里加 id 辅助去重,或者改用 LEFT JOIN 自连接,找“不存在更新时间更大的同分类记录”。哪种都好,关键是要意识到这个场景。

PostgreSQL 中 DISTINCT ON 是更简洁的替代方案

如果你是 PostgreSQL 用户,有更优雅的选项:

SELECT DISTINCT ON (category)
       category, title, content, updated_at
FROM articles
ORDER BY category, updated_at DESC, id DESC;

DISTINCT ON 的用法很直白:对每个 category 只保留一条记录,至于保留哪条,由 ORDER BY 中的后续字段决定。这里 updated_at DESC, id DESC 确保留下最新且 ID 最大的那条。相比窗口函数少一层嵌套,可读性更好。当然,这是 PG 专有语法,跨数据库迁移时要小心。

WHERE 条件不能直接写在窗口函数外层

一个经典错误:比如你想只统计 status = 'published' 的最新记录,结果把 WHERE status = 'published' 放在了最外层。这会导致先取每组最新,再过滤——如果某个分类的最新记录恰好不是 published,这个分类就会消失,而不是退而求其次选次新的 published 记录。逻辑就全歪了。

正确做法是把过滤条件放在子查询或 CTE 内部,确保排序和编号基于已筛选的数据集:

SELECT category, title, content, updated_at
FROM (
  SELECT *,
         ROW_NUMBER() OVER (
           PARTITION BY category 
           ORDER BY updated_at DESC, id DESC
         ) AS rn
  FROM articles
  WHERE status = 'published'  -- ✅ 在这里过滤
) ranked
WHERE rn = 1;

真正麻烦的不是语法本身,而是想清楚“最新”是针对全量数据还是某个子集。这个逻辑偏差,比写错函数名还容易引发线上问题。写 SQL 时多问自己一句:我要的“最新一条”,是在哪些数据里挑?

来源:https://www.php.cn/faq/2676408.html

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
SQL嵌套查询与窗口函数结合使用技巧

SQL嵌套查询与窗口函数结合使用技巧

窗口函数不能直接用于WHERE、GROUPBY或HAVING,需先放入子查询或CTE计算,再在外层引用。典型错误如WHERE中写ROW_NUMBER(),需先用子查询生成编号再过滤。窗口函数排序必须显式写在OVER子句中,CTE比嵌套子查询更可靠。

时间:2026-07-18 18:17
phpMyAdmin导出的Laravel SQL文件生产环境报错原因

phpMyAdmin导出的Laravel SQL文件生产环境报错原因

当使用phpMyAdmin工具导出SQL文件时出现报错,最常见的主要原因包括MySQL版本不匹配(例如8 0特有语法用于5 7)、时区设置语句导致错误、字符集编码不一致等。推荐的解决方法主要包括:注意需要仔细检查文件头部注释内容尤其是版本号,避免使用高版本特有语法,请务必删除SETtime_zone这行语句,并将字符集统一设置为utf8mb4编。

时间:2026-07-18 18:16
MySQL读已提交RC隔离级别减少间隙锁死锁原理

MySQL读已提交RC隔离级别减少间隙锁死锁原理

RC隔离级别默认不加间隙锁,只对命中行加记录锁,通过半一致性读减少锁竞争,且执行后立即释放未命中行的锁,降低了死锁概率。RR隔离级别默认加临键锁,扫描范围全加锁并保持到事务结束,死锁风险更高。但RC在唯一键冲突等场景下仍可能隐式加间隙锁。

时间:2026-07-18 18:16
MySQL批量更新表中特定字段值的方法

MySQL批量更新表中特定字段值的方法

批量更新MySQL表字段,推荐使用CASEWHEN、INSERT…ONDUPLICATEKEYUPDATE或UPDATEJOIN,避免逐行循环。注意语法正确、类型统一,单次更新控制500-1000行,超10万行需分片提交并显式提交事务。

时间:2026-07-18 18:16
phpMyAdmin查询窗口为何无法执行超30秒SQL

phpMyAdmin查询窗口为何无法执行超30秒SQL

phpMyAdmin超时源于$cfg[ ExecTimeLimit ]默认30秒,修改php ini无效,需在config inc php中设为0。MySQL的max_allowed_packet不足也会导致假性超时,需调整配置并重启服务。命令行导入可绕过Web层限制。

时间:2026-07-18 18:16
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 热门数据榜