MySQL 5.6与5.7分组取最新行查询结果差异及适配需求
嘿,这个问题我之前帮朋友排查过,其实不是你的操作误区,而是MySQL 5.7和5.6之间的优化器行为差异导致的!
为什么会出现这个差异?
MySQL 5.7开始引入了一个优化逻辑:如果子查询里的ORDER BY没有搭配LIMIT,优化器会认为这个排序是“无用”的——因为外层查询(比如你这里的分组操作)并不依赖子查询的排序结果,所以直接跳过了排序步骤。而MySQL 5.6的优化器没有这个判断,会严格执行子查询里的ORDER BY,所以结果符合你的预期。
简单说:你的子查询(SELECT * FROM jc_content ORDER BY sort_date DESC)在5.7里根本没执行排序,自然取到的是每组最早的行,而非最新的。
解决办法(按推荐程度排序)
1. 改用标准SQL写法(最稳妥,适配所有版本)
直接通过关联子查询先获取每组的最新sort_date,再匹配对应的行,逻辑清晰且符合SQL标准:
SELECT jc.* FROM jc_content jc INNER JOIN ( -- 先拿到每组的最大sort_date SELECT your_group_column, MAX(sort_date) AS latest_sort_date FROM jc_content GROUP BY your_group_column ) t ON jc.your_group_column = t.your_group_column AND jc.sort_date = t.latest_sort_date;
注意把your_group_column换成你实际用来分组的字段名
2. 给子查询加LIMIT(快速兼容旧写法)
如果不想大改SQL,可以给子查询加一个足够大的LIMIT值(比如MySQL支持的最大无符号整数),这样优化器会认为排序是必要的(因为LIMIT依赖排序结果),不会跳过排序:
SELECT * FROM ( SELECT * FROM jc_content ORDER BY sort_date DESC LIMIT 18446744073709551615 ) cnt GROUP BY your_group_column;
不过要注意:这种写法依赖MySQL的非标准GROUP BY行为(取分组后第一行),如果开启了ONLY_FULL_GROUP_BY模式会报错,所以还是推荐第一种写法。
3. 临时关闭优化器的派生表合并(不推荐)
可以通过会话级参数关闭derived_merge优化,让5.7和5.6的行为一致,但这会影响其他查询的性能,不建议长期使用:
SET optimizer_switch='derived_merge=off';
总结
你之前的写法在5.6能工作是巧合,本质上是依赖了MySQL的非标准行为。5.7的优化其实更符合SQL规范——毕竟没有LIMIT的子查询排序,在SQL标准里是没有意义的。所以最好的方式是改用第一种标准写法,彻底避免版本差异带来的问题。
内容的提问来源于stack exchange,提问作者stephCurry

