最可靠的方法是使用窗口函数ROW_NUMBER() OVER (PARTITION BY group_col ORDER BY create_time DESC)筛选序号为1的行,可精确获取每组最新完整记录,避免重复时间导致多返回或跨组误匹配问题。

用窗口函数加ROW_NUMBER()最可靠
直接用 GROUP BY 配合 MAX(time) 只能取到时间戳,拿不到整行数据;用子查询关联容易漏掉同时间多条记录的情况。真正稳当的做法是用窗口函数给每组按时间倒序编号,再筛出序号为1的行。
-
ROW_NUMBER() OVER (PARTITION BY group_col ORDER BY create_time DESC)是核心,注意ORDER BY必须明确写DESC,否则取到的是最早而非最新 - 如果存在相同
create_time的多条记录,ROW_NUMBER()会强制分出先后,RANK()或DENSE_RANK()则可能并列,导致结果不止一条——除非你真需要所有“并列最新” - MySQL 8.0+、PostgreSQL、SQL Server、Oracle 都支持;SQLite 3.25+ 也支持,但旧版不行
老版本MySQL(5.7及更早)只能靠相关子查询
没有窗口函数时,常见写法是让主表每行去子查询找对应分组的最大时间,再判断本行时间是否等于该最大值。但性能差,且当有重复时间时可能返回多行。
- 典型结构:
WHERE (group_col, create_time) IN (SELECT group_col, MAX(create_time) FROM table GROUP BY group_col) - 必须把
group_col和create_time组成联合条件,单靠create_time IN (SELECT MAX(...))会跨组误匹配 - 如果
create_time字段允许 NULL,MAX()返回 NULL,整个IN判断失效,得额外加IS NOT NULL过滤
LEFT JOIN 自连接方式容易错在ON条件
用表自连接,对每行找“同组但时间更大的记录”,再筛选出不存在更大记录的行。逻辑清晰,但 ON 条件写错就全乱了。
- 正确写法:
ON t1.group_col = t2.group_col AND t1.create_time ,然后 <code>WHERE t2.group_col IS NULL - 常见错误:漏掉
t1.group_col = t2.group_col,变成全局比较;或写成导致永远找不到空匹配 - 如果有重复时间,这种写法会把所有并列最大时间的行都保留下来——这反而是它的优势,但得确认业务是否接受
ORDER BY + LIMIT 1 在分组场景下完全不适用
很多人直觉想用 ORDER BY create_time DESC LIMIT 1,但这只对全表有效,不能自动按组执行。强行套 GROUP BY 会触发 MySQL 的 SQL mode 报错(如 ONLY_FULL_GROUP_BY),或返回非预期字段值。
- MySQL 5.7 默认开启
ONLY_FULL_GROUP_BY,此时SELECT *加GROUP BY group_col ORDER BY create_time DESC LIMIT 1直接报错:Expression #1 of SELECT list is not in GROUP BY clause - 即使关掉该模式,
SELECT中未出现在GROUP BY里的字段(比如id、content)值是随机选取的,不是最新那条的

















