记 MySQL 5.7.5 及以上实现的功能检测依赖报错分析
秉承着 “软件均使用最新版本,学习均从文档入手” 的理念,学习使用 MySQL 我也是在使用最新的大版本 MySQL 8,而在技术发展过程中,一个不可忽视的因素就是兼容性,在保证新特性无误的同时必须确保以前的功能不受影响,如果不能保证这一点,那么许多使用者在迁移的时候就需要考虑很多改动成本,除非不是大改动,一般都会考虑到这一点,比如 HTTP/2 、TLS/1.3 等等。
错误描述及分析
最近为学校写一个选课平台时关系型数据库使用的是 MySQL 8+,使用 Group by 时报了这个错误:
ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'mydb.t.address' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
意思是说 select 列表中包含了非聚合列,其并不函数依赖于 Group by 子句中的列。并且和 sql_mode
这个配置项的 only_full_group_by
值冲突。不过这篇文章并不只是单纯说解决方法,如果想要解决方法请直接移步文末。
可以确定的是同样的使用了 Group by 的 SQL 查询语句在低版本中是正常的但是高版本却不行,猜测可能是版本差异造成的,而且还是因为这个配置项导致的。
所以虽然我们一开始可能看不懂这个报错信息,但是我们通过一些分析已经定位了问题,并且报错的错误码明显(ERROR 1055(42000)),报错提示信息清楚,并不难找到解决方法。
追根溯源
为了更好理解错误原因,举个例子,假定你有这个查询语句:
SELECT name, address, MAX(age) FROM t GROUP BY name;
看起来很正常,但是如果 name 列不是主键,并且表中数据是这样子呢:
name address age
wasabi a 2
wasabi b 2
分组以后 MAX(age) 还相同怎么办,是显示 a 地址还是 b 地址呢?MySQL 这就确定不了了,所以在 MySQL5.7.5 及以上 引入了函数依赖检测,具体描述是:如果 SELECT 列表,HAVING 条件,ORDER BY 列表应用了聚合列,但是并没有在 Group by 子句中命名,就会拒绝查询,如果启用了 ONLY_FULL_GROUP_BY 这个配置项也可以引用非聚合列,可以有多列,但是必须确保每列值唯一,不然会引起冲突。
不过你也可以通过允许任意值来让 MySQL 为你随便选一个返回,比如这样:
SELECT name, ANY_VALUE(address), MAX(age) FROM t GROUP BY name;
再举个例子,你有这些数据,使用如下查询语句:
c1 c2 c3
1 2 A
3 4 B
1 2 C
SELECT DISTINCT c1, c2 FROM t ORDER BY c3;
你应该返回哪行呢,第一行还是第三行?无法确定,这就是 MySQL 解决的问题。
总结
MySQL 在 5.7.5+ 版本实现了函数依赖检测,如果某些列中不存在函数依赖关系,还没有被引用使用的话,就认为可能存在冲突,所以为 sql_mode 配置项增加了一个列表值 ONLY_FULL_GROUP_BY
你可以通过这些命令查看这个配置项的值:
mysql> select @@global.sql_mode;
mysql> select @@session.sql_mode;
+---------------------------------------------------------------+
| @@session.sql_mode |
+---------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION |
+---------------------------------------------------------------+
然后通过 set 来重新设置这个值,把这个列表项去掉即可:
set @@global.sql_mode='STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';