MySQL 5.7 之 默认ONLY_FULL_GROUP_BY语义介绍

SQL92及更早版本不允许查询的选择列表,HAVING条件或ORDER BY列表引用未在GROUP BY子句中命名的非聚集列。例如,此查询在标准SQL92中是非法的,因为name选择列表中的非聚集列不出现在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;

为使查询在SQL92中合法,该name 列必须从选择列表中省略或在GROUP BY子句中命名 。

如果SQL99和更高版本在功能上依赖于GROUP BY列,则允许每个可选功能T301使用这样的非聚合:如果这种关系存在于name和之间 custid,则该查询是合法的。例如,这是 custid一个主要的关键 customers。

MySQL 5.7.5及更高版本实现对函数依赖的检测。如果ONLY_FULL_GROUP_BY启用了 SQL模式(默认情况下),MySQL会拒绝那些select列表,HAVING条件或 ORDER BY列表所引用的查询,这些查询既不是在GROUP BY子句中命名的,也不是在功能上依赖于它们的非聚合列。(在5.7.5之前,MySQL没有检测到函数依赖,并且 ONLY_FULL_GROUP_BY默认情况下是不启用的。关于5.7.5之前的行为的描述,请参阅MySQL 5.6参考手册。

如果ONLY_FULL_GROUP_BY禁用,那么标准SQL使用的MySQL扩展 GROUP BY允许选择列表, HAVING条件或ORDER BY列表引用非聚合列,即使列在功能上并不依赖于GROUP BY列。这会导致MySQL接受前面的查询。在这种情况下,服务器可以自由选择每个组中的任何值,因此除非它们相同,否则所选的值是不确定的,这可能不是您想要的值。此外,每个组的值的选择不能通过添加一个ORDER BY子句来影响。结果集排序是在选择值之后进行的ORDER BY不会影响服务器选择的每个组中的哪个值。ONLY_FULL_GROUP_BY当你知道由于数据的一些属性,在GROUP BY每个组中没有命名的每个非聚集列中的所有值都是相同的,那么禁用 是非常有用的 。

您可以ONLY_FULL_GROUP_BY通过使用 ANY_VALUE()引用nonaggregated列而不禁用相同的效果 。

下面的讨论演示了函数依赖,MySQL在缺少函数依赖时产生的错误消息,以及在没有函数依赖的情况下导致MySQL接受查询的方法。

此查询在ONLY_FULL_GROUP_BY启用时可能无效, 因为address选择列表中的非聚集列未在GROUP BY 子句中命名:

SELECT name, address, MAX(age) FROM t GROUP BY name;
如果name是t或者是唯一NOT NULL列的主键,则该查询是有效的。在这种情况下,MySQL认识到所选列在功能上依赖于分组列。例如,如果name是主键,则其值将确定值,address因为每个组只有一个主键值,因此只有一行。因此,address在组中选择值没有随机性 ,也不需要拒绝查询。

如果name不是主键t或唯一NOT NULL列,则查询无效。在这种情况下,不能推断函数依赖,并发生错误:

mysql> SELECT name, address, MAX(age) FROM t GROUP BY name;
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

如果你知道,对于一个给定的数据集, 每个name值实际上唯一确定的address值,address 是有效的功能依赖 name。要告诉MySQL接受这个查询,你可以使用这个ANY_VALUE()函数:

SELECT name, ANY_VALUE(address), MAX(age) FROM t GROUP BY name;
或者,禁用 ONLY_FULL_GROUP_BY。

前面的例子很简单,但是。特别是,你不太可能在一个主键列上进行分组,因为每个组只包含一行。有关在更复杂的查询中证明函数依赖的附加示例,请参见第12.19.4节“功能依赖的检测”。

如果查询具有聚合函数且没有GROUP BY子句,则在选择列表,HAVING条件或 ORDER BY列表中 ONLY_FULL_GROUP_BY启用时不能有非聚集列:

mysql> SELECT name, MAX(age) FROM t;
ERROR 1140 (42000): In aggregated query without GROUP BY, expression
#1 of SELECT list contains nonaggregated column 'mydb.t.name'; this
is incompatible with sql_mode=only_full_group_by

没有GROUP BY,有一个单一的组,它是不确定的,name为组选择的价值。在这里也 ANY_VALUE()可以使用,如果nameMySQL选择的是非重要的值:

SELECT ANY_VALUE(name), MAX(age) FROM t;
在MySQL 5.7.5及更高版本中,ONLY_FULL_GROUP_BY 也会影响处理使用DISTINCT和的查询 ORDER BY。考虑表的情况下,t有三列c1,c2以及 c3包含这些行:

c1 c2 c3
1  2  A
3  4  B
1  2  C

假设我们执行以下查询,期望结果按以下顺序排序c3:

SELECT DISTINCT c1, c2 FROM t ORDER BY c3;
要排列结果,必须首先消除重复项。但要这样做,我们应该保持第一行还是第三行?这种任意的选择会影响保留的价值c3,这反过来又会影响排序并使其变得任意。为了防止这种问题,即有一个查询 DISTINCT和ORDER BY被拒绝为无效如果任何ORDER BY 表达式不满足这些条件的至少一个:

表达式等于选择列表中的一个

由表达式引用并且属于查询的所选表的所有列都是选择列表的元素

对标准SQL的另一个MySQL扩展允许在HAVING子句中引用选择列表中的别名表达式。例如,以下查询返回 name表中只出现一次的值 orders:

SELECT name, COUNT(name) FROM orders
  GROUP BY name
  HAVING COUNT(name) = 1;
MySQL扩展允许HAVING在聚合列的子句中使用别名 :

SELECT name, COUNT(name) AS c FROM orders
  GROUP BY name
  HAVING c = 1;

注意
在MySQL 5.7.5之前,启用 ONLY_FULL_GROUP_BY禁用这个扩展,因此需要HAVING 使用非混淆表达式来写入子句。

标准SQL仅允许GROUP BY子句中的列表达式,所以像这样的语句是无效的,因为FLOOR(value/100)它是非列表达式:

SELECT id, FLOOR(value/100)
  FROM tbl_name
  GROUP BY id, FLOOR(value/100);

MySQL扩展标准SQL以允许GROUP BY子句中的非列表达式, 并认为前面的语句有效。

标准SQL也不允许在GROUP BY子句中使用别名。MySQL扩展标准SQL以允许别名,所以另一种编写查询的方式如下:

SELECT id, FLOOR(value/100) AS val
  FROM tbl_name
  GROUP BY id, val;

该别名val被认为是该GROUP BY子句中的列表达式。

在GROUP BY子句中存在非列表达式时,MySQL会识别该表达式与选择列表中的表达式之间的等同关系。这意味着在ONLY_FULL_GROUP_BY启用S​​QL模式的情况下,包含的查询GROUP BY id, FLOOR(value/100)是有效的,因为FLOOR()在选择列表中出现相同的 表达式。但是,MySQL不会尝试识别对非GROUP BY列表达式的函数依赖性,因此ONLY_FULL_GROUP_BY即使第三个选择的表达式是子句 中的id列和 FLOOR()表达式 的简单公式,下面的查询也是无效的。 GROUP BY

SELECT id, FLOOR(value/100), id+FLOOR(value/100)
  FROM tbl_name
  GROUP BY id, FLOOR(value/100);

解决方法是使用派生表:

SELECT id, F, id+F
  FROM
    (SELECT id, FLOOR(value/100) AS F
     FROM tbl_name
     GROUP BY id, FLOOR(value/100)) AS dt;

猜你喜欢

转载自blog.csdn.net/guyan0319/article/details/79282616
今日推荐