开发时遇到过几次这个问题,当时随便搜一下,改一改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里,并且选择的字段跟分组依据没有依赖关系

导致分组后会有多个结果,并且每个结果中,这个“意义不明”的字段值是随机的,和分组依据没有对应,依赖的

解决报错:

  1. 使其有依赖性:单个结果集、
  2. 含有多个结果的结果集使用ANY_VALUE()
  3. 关闭该模式

上面的例子是很简单的,毕竟不会在主键上分组,更复杂的可以参看官方文档继续深耕第12.20.4节“函数依赖性检测”。

下面是有关该模式开启后会造成的其他影响

SELECT name, MAX(age) FROM t;

没有GROUP BY,其中一个是聚合列,一个不是
相当于一个组,对于聚合后的结果 name是不确定的,有多个,可以使用ANY_VALUE(name)接受查询

【待续】