您的当前位置:首页 > 娱乐 > sql查询最近一次记录 正文

sql查询最近一次记录

时间:2026-10-17 04:33:31 来源:网络整理 编辑:娱乐

核心提示

凌晨两点,运维群里弹出一条消息:某台服务器CPU飙到95%。我打开数据库,准备查最近一次采集的监控数据,看看是哪个进程在捣鬼。表结构很简单:monitor_data(id, server_id, cp

凌晨两点,询最运维群里弹出一条消息:某台服务器CPU飙到95%。近次记录我打开数据库,询最准备查最近一次采集的近次记录监控数据,看看是询最哪个进程在捣鬼。表结构很简单:monitor_data(id,近次记录 server_id, cpu_usage, collect_time)。问题在于,询最每台服务器每10秒就写入一条记录,近次记录一天下来就是询最8640条。我要的近次记录是“每台服务器最近一次”的那条。

很多人第一反应是询最写子查询:

SELECT * FROM monitor_data WHERE collect_time = (SELECT MAX(collect_time) FROM monitor_data); 

这条SQL有个致命问题——它只返回全表最新的一条记录,而不是近次记录每台服务器各自的最新记录。如果服务器A在10:00:00采集,询最服务器B在10:00:05采集,近次记录你只会看到B的询最数据。A被吞掉了。

正确的做法是用GROUP BY配合聚合函数。但这里有个坑:MySQL在ONLY_FULL_GROUP_BY模式下,SELECT *和GROUP BY server_id会直接报错。你得明确指定要取哪一行的哪些列。

我常用的写法是关联子查询:

SELECT m.* FROM monitor_data m INNER JOIN ( SELECT server_id, MAX(collect_time) AS max_time FROM monitor_data GROUP BY server_id ) t ON m.server_id = t.server_id AND m.collect_time = t.max_time; 

先算出每台服务器的最大时间,再回表关联。逻辑清晰,索引建在(server_id, collect_time)上,速度不慢。但有个隐患:如果同一秒内同一台服务器写入了两条记录(比如重试机制导致重复写入),这个查询会返回两行。业务上如果要求严格去重,还得再加一层GROUP BY或者用ROW_NUMBER()。

说到ROW_NUMBER(),MySQL 8.0之后窗口函数才是更优雅的方案:

SELECT * FROM ( SELECT *, ROW_NUMBER() OVER ( PARTITION BY server_id ORDER BY collect_time DESC ) AS rn FROM monitor_data ) t WHERE rn = 1; 

这个写法我更喜欢。分区、排序、编号,一步到位。就算同一秒有重复记录,rn=1也只取一条。缺点是子查询会扫描全表,数据量上千万时得配合时间范围过滤,比如加个WHERE collect_time > NOW() - INTERVAL 1 DAY。

PostgreSQL用户还有另一种选择:DISTINCT ON。

SELECT DISTINCT ON (server_id) * FROM monitor_data ORDER BY server_id, collect_time DESC; 

简洁到令人发指。但这是PG特有的语法,换到MySQL直接报错。所以跨数据库的项目里,我一般还是用窗口函数,兼容性更好。

回到那个凌晨的故障。我用窗口函数查出每台服务器最近一次CPU数据,发现是某台机器上的日志采集进程卡死了。杀掉进程后,CPU降回正常。整个过程不到五分钟,但如果没有“查最近一次记录”这个动作,可能得翻半天日志。

有个细节值得注意:collect_time字段的类型如果是DATETIME,默认精度只到秒。同一秒内的多条记录,MAX(collect_time)无法区分先后。要么把字段改成DATETIME(3)带毫秒,要么用自增id作为排序依据。我吃过这个亏——监控数据一秒内写了三条,结果取出来的“最新”其实是随机的一条。

还有索引。没有索引的情况下,GROUP BY server_id会走全表扫描,再建临时表排序。数据量大的时候,查询时间从毫秒级变成分钟级。我的习惯是:只要看到GROUP BY和MAX()组合,立刻检查(分组字段, 排序字段)的联合索引是否存在。

最后说个反直觉的点。很多人觉得“最近一次记录”就是ORDER BY collect_time DESC LIMIT 1。这在单台服务器场景下没问题,但多台服务器时,LIMIT 1只会返回全局最新的一条。你得先分组,再取每组第一条。顺序不能反。