线上服务器数据库查询使用了 GROUP BY 竟然报出以下错误
1055 - Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'csc_risk.a.DefaultDate' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by, Time: 0.035000s
将部分数据迁移到本地进行测试时,发现同样的 SQL 语句居然可以正常执行。这就有点意思了——问题到底出在哪里呢?
先来查看本地数据库的版本:
SELECT VERSION()

再检查一下线上数据库的版本,发现同样是 5.7 版本。
5.7.24
原因分析
MySQL 5.7 版本默认启用了 sql_mode = only_full_group_by 属性,正是这个属性导致了上述报错。知道了原因就好办了——接下来就是如何解决它。
在默认安装的 MySQL 5.7.x 版本中,only_full_group_by 模式是开启的。一旦开启,你会发现 group by 的使用变得非常严格:它只能返回分组字段本身,而不能直接返回其他非分组字段。换句话说,group by 几乎变成了 distinct 的加强版,功能范围一下子缩小了很多。
不过,这个模式开启也有其优点——MySQL 提供了一个 any_value(field) 函数,它允许非分组字段出现在 SELECT 中,效果与关闭 only_full_group_by 相同。因此,如果你不想关闭该模式,也可以考虑使用这个函数来实现兼容。
1、查看当前的 sql_mode
SELECT @@sql_mode;
查询出来的结果大致如下:
ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
2、去掉 ONLY_FULL_GROUP_BY,重新设置
SET @@global.sql_mode ='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
3、对于已存在的数据库,还需要单独设置
上述全局设置只对新创建的数据库生效。对于已经存在的数据库,需要在对应的数据库中执行以下命令:
SET sql_mode ='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
注意:以上方式仅作为临时解决方案,数据库重启后就会失效。
修改 MySQL 配置文件(永久生效)
在 Linux 系统下修改 my.cnf,在 Windows 系统下修改 my.ini。在 [mysqld] 段下添加以下配置:
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
总结
遇到 only_full_group_by 报错时,可以从两个方向入手:要么临时关闭该模式,要么修改配置文件使其永久生效。另外,使用 any_value() 函数也是一种优雅的兼容方式。希望这个排查过程能为你提供一些参考。
