SQL查询每个分类下最新更新的一条完整记录
使用窗口函数`ROW_NUMBER()`按分类和更新时间倒序编号并取首行,可获取每个分类的最新完整记录。注意附加`id`排序避免同秒结果不稳定。MySQL老版本可用子查询配合`MAX(updated_at)`自连接,但需警惕同时间多条记录;PostgreSQL的`DISTINCTON`更简洁。过滤条件应置于子查询内,避免逻辑偏差。
先聊一个不少同学踩过的坑:用 GROUP BY 加 MAX(updated_at) 确实能拿到每个分类的最新更新时间戳,但你要的是那条完整记录——时间戳之外的字段全没着落。真正靠谱的解法是窗口函数 ROW_NUMBER() 按分类和更新时间倒序编号,然后只取编号为 1 的行。MySQL 里可以配合 JOIN 子查询,PostgreSQL 则有更简洁的 DISTINCT ON。另外提醒一句:过滤条件放错位置,结果会悄悄出错。

用窗口函数 ROW_NUMBER() 按分类和时间排序取首行
假设表名叫 articles,字段有 category、title、content、updated_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 时多问自己一句:我要的“最新一条”,是在哪些数据里挑?
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
SQL嵌套查询与窗口函数结合使用技巧
窗口函数不能直接用于WHERE、GROUPBY或HAVING,需先放入子查询或CTE计算,再在外层引用。典型错误如WHERE中写ROW_NUMBER(),需先用子查询生成编号再过滤。窗口函数排序必须显式写在OVER子句中,CTE比嵌套子查询更可靠。
phpMyAdmin导出的Laravel SQL文件生产环境报错原因
当使用phpMyAdmin工具导出SQL文件时出现报错,最常见的主要原因包括MySQL版本不匹配(例如8 0特有语法用于5 7)、时区设置语句导致错误、字符集编码不一致等。推荐的解决方法主要包括:注意需要仔细检查文件头部注释内容尤其是版本号,避免使用高版本特有语法,请务必删除SETtime_zone这行语句,并将字符集统一设置为utf8mb4编。
MySQL读已提交RC隔离级别减少间隙锁死锁原理
RC隔离级别默认不加间隙锁,只对命中行加记录锁,通过半一致性读减少锁竞争,且执行后立即释放未命中行的锁,降低了死锁概率。RR隔离级别默认加临键锁,扫描范围全加锁并保持到事务结束,死锁风险更高。但RC在唯一键冲突等场景下仍可能隐式加间隙锁。
MySQL批量更新表中特定字段值的方法
批量更新MySQL表字段,推荐使用CASEWHEN、INSERT…ONDUPLICATEKEYUPDATE或UPDATEJOIN,避免逐行循环。注意语法正确、类型统一,单次更新控制500-1000行,超10万行需分片提交并显式提交事务。
phpMyAdmin查询窗口为何无法执行超30秒SQL
phpMyAdmin超时源于$cfg[ ExecTimeLimit ]默认30秒,修改php ini无效,需在config inc php中设为0。MySQL的max_allowed_packet不足也会导致假性超时,需调整配置并重启服务。命令行导入可绕过Web层限制。
- 热门数据榜
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
相关攻略
2026-07-18 18:17
2026-07-18 18:16
2026-07-18 18:16
2026-07-18 18:16
2026-07-18 18:16
2026-07-18 18:15
2026-07-18 18:15
2026-07-18 18:15
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程

