求MySQL 5.7替代MariaDB 10.4分区取最新记录的SQL方案
解决MySQL 5.7无法使用ROW_NUMBER() OVER(PARTITION BY)的替代方案
嘿,这个兼容性问题我之前也碰到过——MySQL 5.7确实没支持窗口函数(ROW_NUMBER()、PARTITION BY这些都是MySQL 8.0才加进去的),而MariaDB 10.2+就已经支持了,所以会出现你说的运行不了的情况。下面给你几个实用的替代方案,都能实现“获取每个A_id对应的最新B记录”的需求:
方案一:关联子查询(最直观易读)
这个方法逻辑很清晰,先通过子查询找出每个a.id对应的最大Created_at,再关联回原表拿到完整记录,适合数据量不是特别大的场景:
SELECT a.id AS A_id, b.id AS B_id, b.Created_at FROM alpha a JOIN beta b ON b.a_id = a.id WHERE b.Created_at = ( SELECT MAX(Created_at) FROM beta WHERE a_id = a.id );
⚠️ 注意:如果同一个a.id下有多个记录的Created_at完全相同,这个查询会返回所有这些记录。如果你的业务里Created_at是唯一的,或者允许返回同时间的多条记录,这个方案最省心。
方案二:GROUP BY + 关联(确保唯一结果)
如果需要保证每个A_id只返回一条记录(哪怕有相同的Created_at),可以先按a.id分组拿到最大Created_at,再结合b.id的最大值(或其他业务规则)来筛选唯一记录:
SELECT a.id AS A_id, b.id AS B_id, b.Created_at FROM alpha a JOIN ( SELECT a_id, MAX(Created_at) AS max_created, MAX(id) AS max_b_id FROM beta GROUP BY a_id ) b_max ON a.id = b_max.a_id JOIN beta b ON b.a_id = b_max.a_id AND b.Created_at = b_max.max_created AND b.id = b_max.max_b_id;
这里用MAX(id)来在Created_at相同时选id最大的那条,你也可以根据自己的业务需求换成其他聚合规则,比如MIN(id)。
方案三:用户变量模拟窗口函数(大数据量友好)
如果你的数据量很大,子查询可能性能跟不上,可以用MySQL的用户变量来模拟ROW_NUMBER()的分区排序效果:
SELECT A_id, B_id, Created_at FROM ( SELECT a.id AS A_id, b.id AS B_id, b.Created_at, @rn := CASE WHEN @current_a_id = a.id THEN @rn + 1 ELSE 1 END AS rn, @current_a_id := a.id FROM alpha a JOIN beta b ON b.a_id = a.id CROSS JOIN (SELECT @current_a_id := NULL, @rn := 0) vars ORDER BY a.id, b.Created_at DESC ) q WHERE rn = 1;
⚠️ 注意:这个方法依赖于ORDER BY的执行顺序,在MySQL 5.7中是稳定的,但一定要确保排序规则正确,不然可能会出现不符合预期的结果。
你可以根据自己的数据规模、业务规则来选最合适的方案,测试下哪种在你的环境里性能最好~
内容的提问来源于stack exchange,提问作者Mariwa




