专栏名称: 芋道源码
纯 Java 源码分享公众号,目前有「Dubbo」「SpringCloud」「Java 并发」「RocketMQ」「Sharding-JDBC」「MyCAT」「Elastic-Job」「SkyWalking」「Spring」等等
目录
相关文章推荐
芋道源码  ·  工作 6 年,@Transactional ... ·  昨天  
芋道源码  ·  巧用 SpringEvent 解决 ... ·  4 天前  
芋道源码  ·  12月跳槽的兄弟注意了 ·  4 天前  
芋道源码  ·  SpringBoot 实现 License ... ·  4 天前  
51好读  ›  专栏  ›  芋道源码

孤陋寡闻了,原来 MySQL 还能这么写?

芋道源码  · 公众号  · Java  · 2024-12-27 09:30

正文

👉 这是一个或许对你有用的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入芋道快速开发平台知识星球。下面是星球提供的部分资料: 

👉这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号、CRM 等等功能:

  • Boot 仓库:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 仓库:https://gitee.com/zhijiantianya/yudao-cloud
  • 视频教程:https://doc.iocoder.cn
【国内首批】支持 JDK 21 + SpringBoot 3.2.2、JDK 8 + Spring Boot 2.7.18 双版本 

来源:古时的风筝


最近给一个我从来没碰过的老系统加点儿功能,本来连测试环境的数据库一切都很顺利,但是为了保证功能在生产数据上没有问题,就准备把一部分生产数据搞到我本地环境上测一下。

结果,果然出现了问题,代码问题就是这样,总在不经意间来到。

依我我知,想要不出现代码问题,最好的方式就是——不写代码!

出现问题,咱就解决啊,我一看日志控制台一大堆红色报错,不对啊,这都不是我刚加的功能报出来的,这样的话,我就放心了。

摘出来一条错误信息是下面这样的,是 SQL 查询语句报错了,报错信息很明显,就是说GROUP BY 后面的条件没有在前面的 SELECT Columns 列表里。

一看其中有两个关键点:

  1. GROUP BY
  2. sql_mode=only_full_group_by
1055 - Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'c.type' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by, Time: 0.001000s 

我把 SQL 简化了一下就是下面这个样子:

select u.id,u.age from user u GROUP BY u.province;

一看这条 SQL,我心想,这SQL 本身就有问题啊,怎么就在系统中呆了这么长时间,但是在测试和生产环境确实就是正常运行的。

我本地一直用MySQL5.7,再看测试和生产数据库,也是 5.7,就是小版本不太一样,按往常经验,小版本应该不会有这么大影响。

那肯定就是配置的问题,那肯定就是报错信息中提到的 sql_mode=only_full_group_by 这个,原谅我孤陋寡闻了,用了 MySQL 这么久,从来没听过这玩意,而且用 GROUP BY就是为了分组聚合,GROUP BY后面的条件要出现在 SELECT 列表里不是很正常吗,除非有两个列有同样的作用,比如一个名称,一个编码,用编码分组,显示名称。要不然分组的意义在哪里呢?

但是系统已经运行了很长时间了,那这 SQL 一定有他存在的意义,不管那么多了,看问题就好了。

sql_mode

然后我给自己科普了一下 sql_mode。

sql_mode 是 MySQL 的一个系统变量,用来控制 MySQL 服务器的 SQL 语法和行为的处理方式。通过配置不同的 sql_mode 值,MySQL 可以在 SQL 语法检查、数据完整性约束、以及查询处理等方面进行不同的操作。

总之,就是 MySQL 会根据这个配置的内容,来灵活的进行语法检查、数据约束等操作,加入的变量越多,控制的就越严格。

发现从 MySQL 5.7 开始,sql_mode 加了很多变量,ONLY_FULL_GROUP_BY就在其中。

使用 SELECT @@sql_mode;可以查询数据库中 sql_mode 配置的变量有哪些,这是 5.7 版本的默认配置。

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

既然是变量,就是可以改的,所以,这些变量可以动态的增减,或者索性全部去掉。

那肯定就是测试和生产环境改了这个配置了,上去一查,果不其然,那叫一个干净。后来问之前的同事,了解到之前用的是更早的 MySQL 版本,后来统一升级到了 5.7,然后发现这个问题,所以改了配置。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

ONLY_FULL_GROUP_BY

别的不说,只说 ONLY_FULL_GROUP_BY,当数据库中启用了 ONLY_FULL_GROUP_BY 模式后,就要求在 GROUP BY 查询中,SELECT 子句中的每一列都必须要么出现在 GROUP BY 子句中,要么应用聚合函数(如 COUNT()SUM()MAX()MIN() 等)。

这个例子中就是,province这个字段没有在前面 SELECT 的字段列表中。我从刚用 MySQL 时一直都是按照 GROUP BY后面的列必须在前面的查询列中来做的,没想到这个还能改。

select u.id,u.age from user u GROUP BY u.province;

这种不行,下面这个也不行,因为 SELECT 查询列表中的 u.id 不在 GROUP BY 后面的条件中

select u.id,u.age from user u GROUP BY u.age;

改成下面这样才行

select u.id,u.age from user u GROUP BY u.age,u.id;
--  或者
select u.age from user u GROUP BY u.age;

或者,还有一种情况,可以允许 SELECT 中存在 GROUP BY 后面没有的列,就是加 聚合函数。

这应该是最常规的用法了。

select max(u.id),u.age from user u GROUP BY u.age;

除了影响 GROUP BY外,还会影响 ORDER BY,看下面这条语句,当开启 ONLY_FULL_GROUP_BY后,会报错

SELECT DISTINCT
 b.title,
 b.create_time 
FROM
 b_user b 
ORDER BY
 b.create_time DESC,
 b.update_time DESC

报错信息:

1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'b.update_time DESC' at line 24, Time: 0.001000s

因为update_time字段不在 SELECT 后面,当然这还是因为加了 DISTINCT。当关闭 ONLY_FULL_GROUP_BY后,就能正常执行了。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

关闭 ONLY_FULL_GROUP_BY 模式

如果真的碰到从低版本升级上来的,系统中有很多这样不符合  ONLY_FULL_GROUP_BY规范的语句,最省事的办法就是直接关掉。

最省事儿的方法就是直接改 MySQL 配置文件,找到my.cnf配置文件,将其中的 sql_mode 改成下面这样

sql_mode = ""

然后重启就好了。

最后

建议没有特殊情况,还是打开ONLY_FULL_GROUP_BY,这样能保证你的查询结果不会因为你写错 SQL 而出现莫名奇妙的数据,数据库会及时给你抛出错误,避免你对着一串 SQL 查错时一面懵。

除非你能明确地知道你为什么需要把 ONLY_FULL_GROUP_BY关掉。


欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,长按”或“扫描”下方二维码噢

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

文章有帮助的话,在看,转发吧。

谢谢支持哟 (*^__^*)