1. 程式人生 > >MySQL 5.7默認ONLY_FULL_GROUP_BY語義介紹

MySQL 5.7默認ONLY_FULL_GROUP_BY語義介紹

聚集函數 nds glob 由於 depend 修正 this nag date

MySQL 5.7默認ONLY_FULL_GROUP_BY語義介紹

MySQL 彭東穩 1年前 (2017-02-14) 15422次瀏覽 已收錄 1個評論

ONLY_FULL_GROUP_BY是MySQL提供的一個sql_mode,通過這個sql_mode來提供SQL語句GROUP BY合法性的檢查,在MySQL的sql_mode是非ONLY_FULL_GROUP_BY語義時。一條select語句,MySQL允許target list中輸出的表達式是除聚集函數或group by column以外的表達式,這個表達式的值可能在經過group by操作後變成undefined,例如:

1 2 3 4 5 6 7 8 9 10 11 12 mysql> create database test charset utf8mb4; mysql> use test; mysql> create table tt(id int,count int); mysql> insert into tt values(1,1),(1,2),(2,3),(2,4); mysql> select * from tt group by id; +------+-------+ | id | count |
+------+-------+ | 1 | 1 | | 2 | 3 | +------+-------+ 2 rows in set (0.00 sec)

而對於語義限制都比較嚴謹的多家數據庫,如SQLServer、Oracle、PostgreSql都不支持select target list中出現語義不明確的列,這樣的語句在這些數據庫中是會被報錯的,所以從MySQL 5.7版本開始修正了這個語義,就是我們所說的ONLY_FULL_GROUP_BY語義,例如查看MySQL 5.7默認的sql_mode如下:

1 2 mysql> select @@global.sql_mode; ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

去掉ONLY_FULL_GROUP_BY模式,如下操作:

1 mysql> set global sql_mode=‘STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION‘;

我們把剛才的查詢再次執行:

1 2 3 mysql> select id,count from tt group by id; ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column ‘test.tt.count‘ which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

剛才通過的查詢語句被server拒絕掉了!

所以ONLY_FULL_GROUP_BY的語義就是確定select target list中的所有列的值都是明確語義,簡單的說來,在ONLY_FULL_GROUP_BY模式下,target list中的值要麽是來自於聚集函數的結果,要麽是來自於group by list中的表達式的值。但是由於表達式的表現形式非常豐富,對於程序來說,很難精確的確定一些表達式的輸出結果是明確的,比如:

1 2 3 mysql> select count from tt group by id+count,id; ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column ‘test.tt.count‘ which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

在上面的查詢語句中,其實count的值也是能被唯一確定的,但是由於程序無法分析出這種復雜的關系,所以這條查詢也被拒絕掉了。

我們來看下哪些語句是在mysql的ONLY_FULL_GROUP_BY模式下是被支持的。

1 2 3 4 5 6 7 8 mysql> select id+1 from tt group by id+1; +------+ | id+1 | +------+ | 2 | | 3 | +------+ 2 rows in set (0.00 sec)

這條語句target list中的id+1和group by中的id+1是嚴格匹配的,所以mysql認為target list中的id+1是語義明確的,因此該語句可以通過。

但下面這條就無法通過了。

1 2 3 mysql> select id+1 from tt group by 1+id; ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column ‘test.tt.id‘ which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

因此,如果查詢語句中的target list, having condition 或者order by list裏引用了的表達式不是聚集函數,但是和group by list中的表達式嚴格匹配,該語句也是合法的(id+1和id+1是嚴格匹配的,id+1和id+2在mysql認為是不嚴格匹配的, id+1和1+id也是不嚴格匹配的)。

1 2 3 mysql> select id,max(count) from tt group by count; ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column ‘test.tt.id‘ which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

這條query被server拒絕掉了,因為target list中的id沒有出現在聚集函數中,並且也沒有出現在group by list中。

看下面這條語句:

1 2 3 4 5 6 7 8 mysql> select id+1 as a from tt group by a order by id+1; +------+ | a | +------+ | 2 | | 3 | +------+ 2 rows in set (0.00 sec)

mysql允許target list中對於非聚集函數的alias column被group by、having condition以及order by語句引用(version 5.7中允許having condition引用alias column,version 5.6不支持having condition引用alias column),從上面兩條語句可以看出,group by和order by中引用了alias column,並且其等價於基礎列語義。

1 2 3 4 5 6 7 8 9 10 mysql> select id+count from tt group by id,count; +----------+ | id+count | +----------+ | 2 | | 3 | | 5 | | 6 | +----------+ 4 rows in set (0.00 sec)

從上面的語句可以看出,mysql的ONLY_FULL_GROUP_BY模式支持對basic column進行組合但是不支持對於復雜表達式進行組合,這個受限於表達式分析程度。

總結一下:

MySQL對於ONLY_FULL_GROUP_BY語義的判斷規則是,如果group by list中的表達式是basic column,那麽target list中允許出現表達式是group by list中basic column或者alias column的組合結果,如果group by list中的表達式是復雜表達式(非basic column或者alias column),那麽要求target list中的表達式必須能夠嚴格和group by list中的表達式進行匹配,否者這條查詢會被認為不合法。

mysql命令gruop by報錯this is incompatible with sql_mode=only_full_group_by

在mysql 工具 搜索或者插入數據時報下面錯誤:

ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column ‘database_tl.emp.id‘ which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

原因:

看一下group by的語法:

select 選取分組中的列+聚合函數 from 表名稱 group by 分組的列

從語法格式來看,是先有分組,再確定檢索的列,檢索的列只能在參加分組的列中選。

我當前Mysql版本5.7.17,

再看一下ONLY_FULL_GROUP_BY的意思是:對於GROUP BY聚合操作,如果在SELECT中的列,沒有在GROUP BY中出現,那麽這個SQL是不合法的,因為列不在GROUP BY從句中,也就是說查出來的列必須在group by後面出現否則就會報錯,或者這個字段出現在聚合函數裏面。

查看mysql版本命令:select version();

查看sql_model參數命令:

SELECT @@GLOBAL.sql_mode;

SELECT @@SESSION.sql_mode;

發現:

ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

第一項默認開啟ONLY_FULL_GROUP_BY,

解決方法:

1.只選擇出現在group by後面的列,或者給列增加聚合函數;(不推薦)

2.命令行輸入:

set @@GLOBAL.sql_mode=‘‘;

set sql_mode =‘STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION‘;

默認關掉ONLY_FULL_GROUP_BY!

這個時候 在用工具select 一下

SELECT @@sql_mode;

SELECT @@GLOBAL.sql_mode;

發現已經不存在ONLY_FULL_GROUP_BY ,感覺已經OK。但是如果你重啟Mysql服務的話,發現ONLY_FULL_GROUP_BY還是會存在的

想要徹底解決這個問題 就得去改my.ini 配置(如果你們mysql 沒有這個文件,就把my-default.ini 改成my.ini,我這個版本就是沒有my.ini配置問題)

在 [mysqld]和[mysql]下添加

SET sql_mode =‘STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE

MySQL 5.7默認ONLY_FULL_GROUP_BY語義介紹