mysql only_full_group_by问题而引发的对于group by的深入思考

22次阅读

共计 2124 个字符,预计需要花费 6 分钟才能阅读完成。

前言
最近在项目中使用 mysql 的 group by 进行分组查询的场景比较多,其中一次遇到了一个问题,即在开发环境执行一个如下 sql 时 select a,b,max(c) from test_tbl group by a 时是正确且可执行的,但是放到了测试环境就会报如下的错误。
[Err] 1055 – Expression #1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregated column ‘your_tbl…’ which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

问题原因:因为开发环境和测试环境所使用的 mysql 数据库的版本不一样,开发环境使用的时候 5.6.x 而测试环境使用的是 5.7.x,而在 Mysql 的 5.7.x 版本中默认是开启 sql_mode = only_full_group_by,在这个模式下,我们使用分组查询时,出现在 select 字段后面的只能是 group by 后面的分组字段,或使用聚合函数包裹着的字段。在上面的 sql 中,字段 b 既不是 group by 里面的分组字段,也没有被聚合函数包裹着,5.7 以下的版本不进行检查,而 5.7 以上的版本进行了 sql_mode=only_full_group_by 的检查,所以会出现以上的问题,当然解决方法也很简单,将 b 也纳入到分组字段中即可。
这也带出了我们本篇文章主要想将的内容,group by 到底是怎样实现的分组?
我们创建一张如下的数据表
CREATE TABLE `product` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT ‘ 自增主键 ’,
`product_name` varchar(100) NOT NULL COMMENT ‘ 商品名称 ’,
`brand_name` varchar(100) DEFAULT NULL COMMENT ‘ 品牌名称 ’,
`category_name` varchar(100) NOT NULL COMMENT ‘ 商品分类 ’,
`price` decimal(10,4) NOT NULL COMMENT ‘ 价格 ’
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT=’ 测试用的商品表 ’;
表中有如下数据
mysql> select * from product;
+—-+—————+————+—————+——-+
| id | product_name | brand_name | category_name | price |
+—-+—————+————+—————+——-+
| 1 | XiaoMi6X | XiaoMi | 手机 | 2999 |
| 2 | XiaoAi | Lenovo | 电脑 | 8999 |
| 3 | HUAWEI K1 | HUAWEI | 手机 | 1999 |
| 4 | iPhone X | iPhone | 手机 | 9999 |
| 5 | MacBook Pro | Mac | 电脑 | 13999 |
| 6 | iPhone XMAX | iPhone | 手机 | 10999 |
| 7 | HUAWEI Mate20 | HUAWEI | 手机 | 2999 |
+—-+—————+————+—————+——-+
7 rows in set
我们使用 group by 按照品牌类目 (category_name) 进行分组,并获取该分组中的最高价格
mysql> select category_name,max(price) from product group by category_name;
+—————+————+
| category_name | max(price) |
+—————+————+
| 手机 | 10999 |
| 电脑 | 13999 |
+—————+————+
2 rows in set
这个简单的分组 sql 相信也难不倒大家,但是 group by 的分组过程到底是什么样子的呢?

如上图所示
再举一个例子,我们用品牌名称 brand_name 进行分组,并使用聚合函数算出该品牌下的最低价格。
mysql> select brand_name,min(price) from product group by brand_name;
+————+————+
| brand_name | min(price) |
+————+————+
| HUAWEI | 1999 |
| iPhone | 9999 |
| Lenovo | 8999 |
| Mac | 13999 |
| XiaoMi | 2999 |
+————+————+
5 rows in set
同理如下过程

后记
明天就要去印度市场调研了,期待中。。。

正文完
 0