关于MySql的ONLY_FULL_GROUP_BY问题
近日在开发博客的时候遇到了一个问题归档不显示安装后页面看不见找了很多原因都无法解决最后终于从列的一致性上找到了原因。问题解决了之后博客归档页面完美呈现。一、正确代码$archives $db-fetchAll( SELECT DATE_FORMAT(create_time, %Y年%m月) as month, COUNT(*) as count FROM article WHERE status 1 GROUP BY DATE_FORMAT(create_time, %Y年%m月) ORDER BY MIN(create_time) DESC LIMIT 10 );二、错误代码$archives $db-fetchAll( SELECT DATE_FORMAT(create_time, %Y年%m月) as month, COUNT(*) as count FROM article WHERE status 1 GROUP BY YEAR(create_time), MONTH(create_time) ORDER BY YEAR(create_time) DESC, MONTH(create_time) DESC LIMIT 10 );三、代码分析1、这段代码的作用这段代码的作用是从数据库的文章表中按月份统计文章数量生成归档列表。比如2025年05月 → 3篇文章2025年04月 → 1篇文章2025年03月 → 5篇文章2、正确代码解析SELECT DATE_FORMAT(create_time, %Y年%m月) as month, -- 将日期格式化为2025年05月 COUNT(*) as count -- 统计该月份有多少篇文章 FROM article -- 从文章表查询 WHERE status 1 -- 只统计已发布的文章 GROUP BY DATE_FORMAT(create_time, %Y年%m月) -- 按格式化后的月份分组 ORDER BY MIN(create_time) DESC -- 按月份倒序排列最新的在前 LIMIT 10 -- 只取最近10个月3、原来的错误代码SELECT DATE_FORMAT(create_time, %Y年%m月) as month, COUNT(*) as count FROM article WHERE status 1 GROUP BY YEAR(create_time), MONTH(create_time) -- ❌ 问题在这里 ORDER BY YEAR(create_time) DESC, MONTH(create_time) DESC四、错误原因ONLY_FULL_GROUP_BY1、什么是ONLY_FULL_GROUP_BY这是 MySQL 的一种严格模式它要求在GROUP BY中出现的列必须和SELECT中的非聚合列完全一致。2、错误分析SELECT DATE_FORMAT(create_time, %Y年%m月) as month, -- 非聚合列格式化后的 COUNT(*) as count -- 聚合列没问题 FROM article GROUP BY YEAR(create_time), MONTH(create_time) -- 按年、月分组问题SELECT中的非聚合列是DATE_FORMAT(create_time, %Y年%m月)GROUP BY中的列是YEAR(create_time), MONTH(create_time)这两者不相等MySQL 无法确定DATE_FORMAT(create_time, %Y年%m月)是否和YEAR(...), MONTH(...)一一对应。3、举例说明假设有两条记录记录1create_time 2025-05-01 10:00:00记录2create_time 2025-05-15 14:30:00按YEAR(create_time), MONTH(create_time)分组它们属于同一组都是2025年5月。但DATE_FORMAT(create_time, %Y年%m月)对两条记录都返回2025年05月这没问题。但问题是MySQL 的严格模式不信任这种转换它要求SELECT中的非聚合列必须直接出现在GROUP BY中不能通过函数转换。五、修复方法的原理GROUP BY DATE_FORMAT(create_time, %Y年%m月)为什么这样就能工作因为现在SELECT中的非聚合列和GROUP BY中的列完全一致了SELECT 中的非聚合列GROUP BY 中的列DATE_FORMAT(create_time, %Y年%m月)DATE_FORMAT(create_time, %Y年%m月)MySQL 可以明确知道按这个表达式分组每个组的值是唯一的。六、关于ORDER BY MIN(create_time) DESCORDER BY MIN(create_time) DESC因为我们是按月份分组不是按具体时间。MIN(create_time)取每个组中最早的日期2025年05月组 →MIN(create_time)2025-05-01组内最早的2025年04月组 →MIN(create_time)2025-04-03按MIN(create_time)倒序排列结果就是最新月份在前面。七、对比两种写法写法优点缺点GROUP BY YEAR(...), MONTH(...)性能稍好使用索引严格模式下报错GROUP BY DATE_FORMAT(...)兼容严格模式可能无法使用索引总结问题原因解决归档页面报错MySQL 严格模式要求GROUP BY和SELECT中的非聚合列一致改用GROUP BY DATE_FORMAT(...)一句话严格模式下GROUP BY后面跟什么SELECT中非聚合列就必须是什么不能有函数转换的差异。✅