开发时遇到过几次这个问题,当时随便搜一下,改一改sql完事,最近又碰到了,感觉要深入研究一下
大部分博客关于这个问题讲的基本都是关于SQL-92的
不允许 select、HAVING或ORDER BY 未在GROUP BY 子句中出现的字段,或该字段不是聚合列
官方示例:
SELECT o.custid, c.name, MAX(o.payment)
FROM orders AS o, customers AS c
WHERE o.custid = c.custid
GROUP BY o.custid;
要使以上查询在SQL-92中合法,必须去掉name列,或者在GROUP BY 子句中对该列命名
SQL-99及更高版本允许(...)了一些新特性:如果某些字段和GROUP BY中的列存在依赖关系,则查询是合法的
MySQL 5.75+ 版本实现了这种依赖的检测,并且默认开启了only_full_group_by
模式
即 select、having、orderby 的列是非聚合状态,并且既不在GROUP BY子句中命名,也不存在依赖关系,则MySQL会拒绝查询
可以看到这个模式包含了SQL-99的特性
什么是依赖关系?
先看一个官方示例:
SELECT name, address, MAX(age) FROM t GROUP BY name;
当 ONLY_FULL_GROUP_BY
启用时, 上面的查询可能无效
- 如果
name
是主键,或者唯一非空列,则该查询有效。因为根据name
分组的话,每组的结果只有一行,对应的address
中的值没有随机性,也只有一个,不会拒绝查询 - 如果
name
不是主键、唯一非空,则根据name
分组的数据,address
的值存在多种选择性,查询被拒绝 - 如果每个name值实际上对应着唯一的address(存在依赖关系),即通过name分组后得到的结果集,其中的address是相同的
则可以使用ANY_VALUE()
告诉MySQL接受查询
SELECT name, ANY_VALUE(address), MAX(age) FROM t GROUP BY name;
当ONLY_FULL_GROUP_BY
禁用时:
这将导致MySQL接受上面的查询,服务器可以自由选择结果集中的任何值,因此除非他们相同,否则所选择的列的值是不确定的
此外,在使用order by条件时,结果集排序在选择值后发生,不会影响服务器选择每个组的哪个值
总结
上面的例子报错是因为select的字段不在聚合函数里,不在GROUP BY里,并且选择的字段跟分组依据没有依赖关系
导致分组后会有多个结果,并且每个结果中,这个“意义不明”的字段值是随机的,和分组依据没有对应,依赖的
解决报错:
- 使其有依赖性:单个结果集、
- 含有多个结果的结果集使用ANY_VALUE()
- 关闭该模式
上面的例子是很简单的,毕竟不会在主键上分组,更复杂的可以参看官方文档继续深耕第12.20.4节“函数依赖性检测”。
下面是有关该模式开启后会造成的其他影响
SELECT name, MAX(age) FROM t;
没有GROUP BY,其中一个是聚合列,一个不是
相当于一个组,对于聚合后的结果 name是不确定的,有多个,可以使用ANY_VALUE(name)接受查询
【待续】