关于MySql的ONLY_FULL_GROUP_BY问题

近日,在开发博客的时候,遇到了一个问题,归档不显示,安装后页面看不见,找了很多原因,都无法解决,最后终于从列的一致性上找到了原因。问题解决了之后,博客归档页面完美呈现。

一、正确代码:

sql 复制代码
$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
");

二、错误代码:

sql 复制代码
$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、正确代码解析

sql 复制代码
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、原来的错误代码

sql 复制代码
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_BY

1、什么是 ONLY_FULL_GROUP_BY?

这是 MySQL 的一种严格模式,它要求:

在 GROUP BY 中出现的列,必须和 SELECT 中的非聚合列完全一致。

2、错误分析

sql 复制代码
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、举例说明

假设有两条记录:

  • 记录1:create_time = '2025-05-01 10:00:00'

  • 记录2:create_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 中,不能通过函数转换。


五、修复方法的原理

sql 复制代码
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) DESC

sql 复制代码
ORDER 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 中非聚合列就必须是什么,不能有函数转换的差异。 ✅

相关推荐
啵啵啵鱼34 分钟前
对数据的增删改查
数据库·mysql·ai·idea·ai编程·navicat·obsidian
靠沿39 分钟前
MySQL数据库基础——索引的使用&alter、explain关键字
java·数据库·mysql
赵渝强老师41 分钟前
【赵渝强老师】Oracle的行地址:ROWID
数据库·oracle
重生之高冷汉堡包44 分钟前
什么是数据中心代理?原理拆解、类型区别、应用场景与选型指南
服务器·网络·数据库·安全·ip
Mortalbreeze1 小时前
MySQL 基础篇(八):复合查询之多表查询、内外连接、子查询
数据库·mysql
Xxtaoaooo1 小时前
NocoDB在线表格数据库:fnOS部署、MySQL可视化与远程协作实操
数据库·mysql·docker·cpolar·nocodb
张继雁1 小时前
30~50MPa数控高压清洗:前端分级过滤逻辑,抑制硬质磨粒对喷嘴冲蚀
大数据·数据库·人工智能·机器学习·创业创新
言乐61 小时前
PythonLOLM自动生成复盘报告模型
开发语言·前端·数据库·人工智能·python
倔强的石头_1 小时前
从 KStudio 到 KSQL Developer:国产数据库的 SQL 编辑器,已经卷到这个程度了
数据库
JavaEdge.1 小时前
设定索引方法和检索设置
数据库