
本文介绍在 mysql 中使用窗口函数 row_number() 按 study_id 分组,高效筛选每组中 created_at(或 last_completion_date)时间最近的两条完整记录,并提供可执行示例、注意事项及性能优化建议。
本文介绍在 mysql 中使用窗口函数 row_number() 按 study_id 分组,高效筛选每组中 created_at(或 last_completion_date)时间最近的两条完整记录,并提供可执行示例、注意事项及性能优化建议。
在数据分析与调度监控场景中,常需为每个业务实体(如 study_id)提取其最新若干次操作记录(例如最近两次成功完成的 cron 任务)。由于原始表 analytics_cron_refresh_time 中存在多条同 study_id、不同 created_at 和 status 的记录,直接使用 GROUP BY 或传统 JOIN 很难精准控制“每组取 N 条”的逻辑——尤其当需要保留完整行数据(而非仅聚合字段)时。
推荐方案是使用 窗口函数 ROW_NUMBER()(MySQL 8.0+ 原生支持),它能为每个 study_id 分区内按时间降序编号,再通过外层过滤快速定位前两名:
SELECT *
FROM (
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY study_id
ORDER BY created_at DESC
) AS rn
FROM analytics_cron_refresh_time
WHERE status = 'Complete'
) ranked
WHERE rn <p>✅ <strong>关键说明:</strong> </p>
- 使用
created_at(而非last_completion_date)作为排序依据更符合“最近插入记录”的业务语义;若需按实际完成时间排序,可替换为ORDER BY last_completion_date DESC。 -
PARTITION BY study_id确保编号在每个 study_id 内独立重置;ORDER BY ... DESC保证最新记录排在最前(rn = 1)。 - 外层
WHERE rn 精确截取每组最多 2 条,避免 <code>HAVING COUNT(*) > 2等易出错的聚合误用。
⚠️ 注意事项:
- 此写法要求 MySQL 版本 ≥ 8.0。若使用 MySQL 5.7 或更低版本,需改用变量模拟或自连接(性能较差,不推荐);
- 为提升查询效率,建议在
(study_id, created_at)上建立联合索引(已存在idx_study_id,但未覆盖created_at,可优化为KEY idx_study_created (study_id, created_at)); - 若存在
created_at完全相同的记录,ROW_NUMBER()会强制赋予不同序号(稳定但非业务意义去重);如需相同时间视为并列,应改用RANK()并配合去重逻辑。
? 进阶提示:
如需同时获取“最新一条 Complete + 最新一条 In Progress”,可扩展为条件分区或使用 UNION ALL 分别查询后合并;若需分页查看各 study_id 的历史批次,可在子查询中增加 rn 字段用于前端标识。
掌握此模式后,类似“每个用户最近 3 笔订单”“每设备最近 5 次上报”等需求均可复用同一范式,兼具简洁性、可读性与执行效率。










