一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

MySQL中如何查询出每个分组中ID最大的一条记录?

时间:2026-09-03 19:30:53 编辑:袖梨 来源:一聚教程网

应使用子查询+JOIN或ROW_NUMBER()窗口函数获取每组最大ID的完整记录,而非GROUP BY+MAX(id),因后者仅返回ID值且严格模式下报错ERROR 1055。

用 GROUP BY + 子查询找每组最大 ID 的记录

直接 GROUP BY 无法返回整行数据,只配合 MAX(id) 只能拿到 ID 值,拿不到对应那条记录的其他字段。常见错误是写成 SELECT *, MAX(id) FROM table GROUP BY category —— 这在严格模式下会报错 ERROR 1055,因为非聚合字段没出现在 GROUP BY 中。

稳妥做法是先用子查询找出每组最大 ID,再跟原表做 JOIN

SELECT t1.* FROM my_table t1INNER JOIN (SELECT category, MAX(id) AS max_id FROM my_table GROUP BY category) t2 ON t1.category = t2.category AND t1.id = t2.max_id;
  1. 确保 categoryid 上有联合索引(如 INDEX(category, id)),否则子查询和 JOIN 都可能慢
  2. 如果有多个记录 ID 相同(极少见),这条语句会返回全部——如果必须唯一,得加 ORDER BY id DESC LIMIT 1 到子查询里再用 ROW_NUMBER()
  3. MySQL 5.7 默认开启 ONLY_FULL_GROUP_BY,所以别指望靠 SQL_MODE 关掉来“绕过”错误

MySQL 8.0+ 推荐用 ROW_NUMBER() 窗口函数

更直观、可控性更强,尤其当需要“每组最新一条”(按时间戳而非 ID)时,ROW_NUMBER() 是标准解法。

SELECT * FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY category ORDER BY id DESC) AS rnFROM my_table) ranked WHERE rn = 1;
  1. PARTITION BY category 对应分组字段,ORDER BY id DESC 决定哪条排第一
  2. 如果 ID 不唯一但你想取时间最新的,把 ORDER BY id DESC 换成 ORDER BY created_at DESC, id DESC 防止并列
  3. 注意:窗口函数不能用在 WHERE 子句里,必须套一层子查询或 CTE

避免用 WHERE IN (SELECT MAX...) 的陷阱

有人图省事写成:SELECT * FROM my_table WHERE id IN (SELECT MAX(id) FROM my_table GROUP BY category) —— 看似简洁,实际危险。

  1. 如果不同 category 下碰巧有相同最大 id(比如两组都最大是 100),这条语句会误召回多条记录
  2. 子查询结果集没有关联上下文,IN 无法保证“每个 category 只匹配自己组内的最大 id”
  3. 性能通常比 JOIN 差,特别是数据量大时,MySQL 很难优化这个 IN 子查询

ORDER BY + GROUP BY 在旧版本里的误导性用法

MySQL 5.6 或更低版本中,有人利用“隐式 GROUP BY 行为”加 ORDER BY 来“碰运气”取首行,例如:SELECT * FROM my_table ORDER BY id DESC GROUP BY category

  1. 这是未定义行为,MySQL 不保证返回的是每组中 id 最大的那条;实际结果依赖执行计划、存储引擎、甚至数据插入顺序
  2. MySQL 5.7+ 已禁用该语法,会直接报错 ERROR 3029(除非关掉 sql_mode,但不推荐)
  3. 即使当前跑通,升级后立刻失效,线上隐患极大

真正要稳定取每组最大 ID 对应的完整记录,就老实用 JOINROW_NUMBER()。ID 字段是否自增、是否唯一、是否有 NULL 值,都会影响结果——动手前先 SELECT COUNT(*), COUNT(id), COUNT(DISTINCT id) FROM my_table 看一眼数据质量。

热门栏目