MySQL中没有group by子句时的聚集函数

首先从一个无意中运行的例子讲起。数据表结构及数据如下(建表语句及插入语句就不提供了)。

看下面一段SQL语句及运行结果:

 从这个结果我们可以发现两件事情。第一,这条查询语句中没有group by子句,select子句中的非聚合字段没有出现在group by子句中,这与我们一贯的认知不同。类似语法的SQL查询语句在hive中是报错的(在MySQL也有可能报错,后面会详细说)。第二,这从这条语句的执行结果可以看出,SQL查询语句中的聚合函数字段执行完成,而非聚合字段似乎返回了满足where条件的第一条记录的order_id的值。但该order_id对应的(original_value-discount)的值并不是我们所求的max结果。

下面来分析一为什么会出现这中情况及结果。

通常MySQL5.7及以上版本中sql_mode是默认设置了ONLY_FULL_GROUP_BY模式的。一旦设置了该模式,会将两类SQL查询语句判定为不合法,具体如下:

  • 第1类,select子句、having子句、order by子句中的非聚合函数字段没有group by子句的查询语句。
  • 第2类,select子句、having子句、order by子句中的非聚合函数字段与group by 子句中字段没有函数依赖(functional dependency)。

再来看上面那段SQL语句,没有group by子句,也就意味着select 子句中的非聚合函数字段order_id没有出现在group by子句中,也就不存在函数依赖关系。所以我们可以推测我的MySQL的sql_mode中没有设置ONLY_FULL_GROUP_BY模式。但是在MySQL 5.7及以上版本中,ONLY_FULL_GROUP_BY模式是默认开启的,而关掉该模式可以在my.ini文件中修改sql_mode参数。具体如下:

MySQL8.0中的若是没有my.ini文件,可以参考https://www.cnblogs.com/RayWang/p/9347874.html 。

接着在看在关闭了ONLY_FULL_GROUP_BY模式下,会如何处理在其开启状态下判定为不合法的两类SQL查询语句。

  • 首先,非聚合字段没有出现在group by 子句中。

如果select子句中有未出现在group by子句中的普通字段,这是服务器或从每个组中随机选择一个值输出。

此时,上述SQL查询语句等价于如下代码:

  • 其次,非聚合字与group by中的字段不存在函数依赖关系。

先来看看什么是函数依赖。MySQL函数依赖是指:X能够决定Y,即当X确定之后,Y也就确定了,则我们说Y函数依赖X。 以下面代码为例:

 从对原始数据的分析中可以发现,order_id其实是表的主键,那自然当按order_id进行分组之后,就已经在每个分组内部完成了cust_id的分组。所以上述代是可以运行的。但是字段之间的函数依赖只能基于键,基于相等性及基于视图建立。其他情况,及时X唯一确定了一个Y,也不能算是函数依赖。如下:

在上述SQL代码中,一个city,只会对一个area,但仍然提示1055错误。

参考资料:

1. https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html

2. https://dev.mysql.com/doc/refman/5.7/en/group-by-functional-dependence.html

### MySQL 8.0 GROUP BY 报错解决方案 在 MySQL 8.0 及以上版本中,默认启用了 `sql_mode=only_full_group_by` 模式,这使得 SQL 查询中的 `SELECT` 列必须全部出现在 `GROUP BY` 子句中,或者是聚合函数的结果。当查询违反这一规则,会抛出错误提示。 #### 方法一:修改 SQL 模式禁用 ONLY_FULL_GROUP_BY 可以通过调整 MySQL 配置文件或通过命令行来改变 SQL 模式的设置,从而移除 `ONLY_FULL_GROUP_BY` 的限制: 1. **编辑配置文件** 修改 MySQL 的配置文件(通常是 my.ini 或者 my.cnf),找到 `[mysqld]` 下面添加如下内容并保存重启服务生效[^1]: ```ini [mysqld] sql-mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' ``` 2. **临更改当前会话模式** 如果不想永久性地改动全局参数,在单次连接内也可以动态设定 SQL mode 来绕过这个问题: ```sql SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY','')); ``` 注意这种方式只影响到当前用户的本次登录期间有效[^4]. #### 方法二:修正查询语句使其符合标准 另一种更推荐的做法是按照 SQL 标准重构原始查询逻辑,确保所有非聚集字段都包含于分组依据之中;或者利用特定的聚合函数处理这些额外列的数据。例如可以采用 `ANY_VALUE()` 函数包裹那些不需要严格依赖关系的字段值,以此规避此问题的发生[^5]: ```sql SELECT department_id, ANY_VALUE(employee_name), MAX(salary) FROM employees GROUP BY department_id; ``` 这种方法不仅解决了兼容性的难题,同也提高了数据的一致性和准确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值