MySQL錯誤-this is incompatible with sql_mode=only_full_group_by完美解決方案
專案場景:
有時候,遇到資料庫重複資料,需要將資料進行分組,並取出其中一條來展示,這時就需要用到group by語句。
但是,如果mysql是高版本,當執行group by時,select的欄位不屬於group by的欄位的話,sql語句就會報錯。報錯資訊如下:
Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column ‘資料庫名.表名.欄位名’ which is not functionally dependent on columns in GROUP BY clause; thisis incompatible with sql_mode=only_full_group_by
問題描述:
1.表結構
CREATE TABLE `t_iov_help_feedback` ( `ID` INT(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID', `USER_ID` INT(255) DEFAULT NULL COMMENT '使用者ID', `problems` VARCHAR(255) DEFAULT NULL COMMENT '問題描述', `last_updated_date` DATETIME DEFAULTNULL COMMENT '最後更新時間', PRIMARY KEY (`ID`) ) ENGINE=INNODB DEFAULT CHARSET=utf8;
2.表資料
3.sql語句
1)查詢group by的欄位(正常)
SELECT USER_ID FROM t_iov_help_feedback GROUP BY USER_ID;
SELECT MAX(ID),USER_ID FROM t_iov_help_feedback GROUP BY USER_ID;
2)查詢非group by的欄位(報錯)
報錯什麼意思呢?
一句話概括:“錯誤程式碼1055與sql_mode = only_full_group_by不相容”
翻譯:
“錯誤程式碼:1055。SELECT列表的表示式#1不在GROUP BY子句中,並且包含非聚合列’test.t_iov_help_feedback.ID’,它在功能上不依賴於GROUP BY子句中的列; 這與sql_mode = only_full_group_by不相容”
原因分析:
一、原理層面
這個錯誤發生在mysql 5.7.5 版本及以上版本會出現的問題:
mysql 5.7.5版本以上預設的sql配置是:sql_mode=“ONLY_FULL_GROUP_BY”,這個配置嚴格執行了"SQL92標準"。
很多從5.6升級到5.7時,為了語法相容,大部分都會選擇調整sql_mode,使其保持跟5.6一致,為了儘量相容程式。
二、sql層面 在sql執行時,出現該原因,簡單來說就是: 由於開啟了ONLY_FULL_GROUP_BY的設定,如果select 的欄位不在 group by 中, 並且select 的欄位未使用聚合函式(SUM,AVG,MAX,MIN等)的話,那麼這條sql查詢是被mysql認為非法的,會報錯誤…
驗證是否此原因:1.查詢資料庫版本的語句
SELECT VERSION();
可以看到,我這裡資料庫版本是:8.0.16,大於5.7.5了
驗證是否此原因:2. 檢視sql_mode的語句
select @@GLOBAL.sql_mode;
查詢出來的值為
ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
可以看到,sql_mode開啟了only_full_group_by 屬性
解決方案:
解決方案一:使用函式ANY_VALUE()包含報錯欄位
將上述報錯語句改成:
SELECT ANY_VALUE(ID),USER_ID,ANY_VALUE(problems),ANY_VALUE(last_updated_date) FROM t_iov_help_feedback GROUP BY USER_ID;
可以看到,結果能正常查詢了,根據需要自己改查詢欄位的別名就行。
ANY_VALUE()函式說明:
MySQL有any_value(field)函式,它主要的作用就是抑制ONLY_FULL_GROUP_BY值被拒絕。 這樣sql語句不管是在ONLY_FULL_GROUP_BY模式關閉狀態還是在開啟模式都可以正常執行,不被mysql拒絕。 any_value()會選擇被分到同一組的資料裡第一條資料的指定列值作為返回資料。 官方有介紹,地址:https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_any-value
解決方案二:通過sql語句暫時性修改sql_mode
去掉ONLY_FULL_GROUP_BY,重新設定值
SET @@global.sql_mode ='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
上面是改變了全域性sql_mode,對於新建的資料庫有效。對於已存在的資料庫,則需要在對應的資料庫下執行:
SET sql_mode ='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
問題:
重啟mysql資料庫服務之後,ONLY_FULL_GROUP_BY還會出現,所以這只是暫時性的。
備註:
網上有些朋友提供的sql語句如下:
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
但是卻執行不了,報sql語法錯誤:
這時只需要加上單引號即可:
但是,添加了單引號仍然報錯:
解決方案三:通過配置檔案永久修改sql_mode
mysql安裝在伺服器上和安裝在本地,修改配置檔案的方式有點區別。
1、Linux下修改配置檔案
1)登入進入MySQL
使用命令 mysql -u username -p 進行登陸,然後輸入密碼,輸入SQL:
show variables like ‘%sql_mode’;
2)編輯my.cnf檔案
檔案地址一般在:/etc/my.cnf,/etc/mysql/my.cnf
使用vim命令編輯檔案,不知道vim命令怎麼使用的,可以參考我的另外篇文章:Linux中使用vi工具進行文字編輯
找到sql-mode的位置,去掉ONLY_FULL_GROUP_BY
然後重啟MySQL;
有的my.cnf中可能沒有sql-mode,需要追加:
sql-mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
注意要加入到[mysqld]下面,如加入到其他地方,重啟後也不生效,具體的如下圖:
3)修改成功後重啟MySQL服務
service mysql restart
重啟好後,再登入mysql,輸入SQL:show variables like ‘%sql_mode’; 如果沒有ONLY_FULL_GROUP_BY,就說明已經成功了
如果還不行,那麼只保留STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION 即可
追加內容為:
sql-mode=STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
2、window下修改配置檔案
1)找到mysql安裝目錄,用記事本直接開啟my.ini檔案
2)編輯my.cnf檔案,在[mysql]標籤下追加內容
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
3)重啟mysql 服務
備註:
網上有些提供了sql_mode的值,卻導致重啟mysql服務啟動不了
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
這時,只需要將sql_mode 值中 “NO_AUTO_CREATE_USER” 這個屬性去掉即可。