1、sql_mode=only_full_group_by 導致的語法錯誤問題 MySQLSyntaxErrorException
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'db.table.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
該問題是因為MySQL Server 默認開啟了 sql_mode=only_full_group_by 模式,此模式要求 group by 字段必須出現在查詢項中(select),否則就會報出該錯誤。解決的方式也很簡單見(5),借着這個異常問題,借機在本文就來學習一下,MySQL 5.7 中的 sql_mode。
2、簡介
MySQL服務器可以在不同的SQL模式下運行,並且可以針對不同的客戶端以不同的方式應用這些模式,具體取決於sql_mode系統變量的值。可以設置全局SQL模式以匹配站點服務器的操作要求,每個應用程序也可以設置會話級別的SQL模式來滿足自己的需求。
sql_mode 模式會影響MYSQL語法及執行數據的驗證檢查。這使得在不同環境中使用MySQL以及將MySQL與其他數據庫服務器一起使用變得更加容易。
3、MySQL 5.7 和 MySQL 5.8 中默認的sql_mode
3.1、查詢sql_mode的方式
查詢全局sql_mode
SELECT @@GLOBAL.sql_mode;
查詢當前會話sql_mode
SELECT @@SESSION.sql_mode;
3.2、MySQL 5.7中的默認SQL模式:
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
MySQL 5.7.5 中默認SQL模式添加了 ONLY_FULL_GROUP_BY和STRICT_TRANS_TABLES
MySQL 5.7.7 中默認SQL模式添加了 NO_AUTO_CREATE_USER
MySQL 5.7.8 中默認SQL模式添加了 ERROR_FOR_DIVISION_BY_ZERO,NO_ZERO_DATE 和 NO_ZERO_IN_DATE
3.3、MySQL 5.8中的默認SQL模式(去掉了 NO_AUTO_CREATE_USER):
ONLY_FULL_GROUP_BY, STRICT_TRANS_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, NO_ENGINE_SUBSTITUTION.
4、MySQL 5.7 中 sql_mode 的變化
1、在MySQL 5.7.22中,這些SQL模式已被棄用,將在未來版本的MySQL中刪除:
DB2,MAXDB,MSSQL,MYSQL323,MYSQL40,ORACLE,POSTGRESQL,NO_FIELD_OPTIONS,NO_KEY_OPTIONS,NO_TABLE_OPTIONS。
2、在MySQL 5.7中,默認情況下啟用了 ONLY_FULL_GROUP_BY SQL 模式,因為GROUP BY處理變得更加復雜,包括檢測功能依賴性。
3、但是你會發現啟用 ONLY_FULL_GROUP_BY 會導致現有應用程序的查詢被拒絕,解決辦法如下:
- GROUP BY列包含在select 查詢項中,或者使用ANY_VALUE()引用非聚合列
- 如果無法修改有問題的查詢(例如,如果它是由第三方應用程序生成),請在服務器啟動時將sql_mode系統變量設置為不啟用 ONLY_FULL_GROUP_BY。
4、在MySQL 5.7中,不推薦使用ERROR_FOR_DIVISION_BY_ZERO,NO_ZERO_DATE和NO_ZERO_IN_DATE SQL模式。長期計划是將三種模式包含在嚴格的SQL模式中,並在未來的MySQL版本中將它們作為顯式模式刪除。為了使MySQL 5.7與MySQL 5.6嚴格模式(strict mode)兼容並為受影響的應用程序提供額外的修改時間,以下情況適用:
- ERROR_FOR_DIVISION_BY_ZERO,NO_ZERO_DATE和NO_ZERO_IN_DATE不是嚴格SQL模式的一部分,但它們與嚴格模式一起使用。提醒一下,如果啟用它們而不啟用嚴格模式,則會發出警告,反之亦然。
- 默認情況下啟用ERROR_FOR_DIVISION_BY_ZERO,NO_ZERO_DATE和NO_ZERO_IN_DATE。
通過以上方式的更改,默認情況下仍會啟用更嚴格的數據檢查,但可以在當前需要或必須執行此操作的環境中禁用各個模式。
5、設置 sql_mode
sql_mode 的設置有三種方式,分別是啟動命令行設置、配置文件設置、運行時設置,多個模式之間使用逗號隔開
5.1、啟動命令行設置
服務啟動時,在命令行使用 --sql-mode =“modes” (設置sql_mode) 或 --sql-mode =“” (清空sql_mode)
5.2、配置文件設置
在Unix操作系統下配置文件 my.cnf 設置 sql-mode =“modes” (設置sql_mode) 或 sql-mode =“”(清空sql_mode)
在Windows系統下配置文件 my.ini 中設置 sql-mode =“modes” 或 sql-mode =“”(清空sql_mode)
5.3、運行時設置
要在運行時更改SQL模式,使用SET語句設置全局或會話sql_mode系統變量:
SET GLOBAL sql_mode = 'modes';
SET SESSION sql_mode = 'modes';
設置GLOBAL變量需要SUPER權限,作用於修改后新連接的所有客戶端的操作。 設置SESSION變量僅影響當前客戶端。 每個客戶端都可以隨時更改其會話sql_mode值。
SQL模式和用戶定義的分區【重點】
在創建數據並將數據插入分區表后更改服務器SQL模式可能會導致此類表的行為發生重大更改,並可能導致數據丟失或損壞。強烈建議您在使用用戶定義的分區創建表后永遠不要更改SQL模式。復制分區表時,主服務器和從服務器上的不同SQL模式也會導致問題。 為獲得最佳結果,應始終在主服務器和從服務器上使用相同的服務器SQL模式。
6、sql_mode 常用值
此處只列出部分值,並非全部
ONLY_FULL_GROUP_BY
對於GROUP BY聚合操作,如果在SELECT中的列,沒有在GROUP BY中出現,那么這個SQL是不合法的,因為列不在GROUP BY從句中。
NO_AUTO_VALUE_ON_ZERO
該值影響自增長列的插入。默認設置下,插入0或NULL代表生成下一個自增長值。如果用戶希望插入的值為0,該列又是自增長的,那么這個選項就有用了。
STRICT_TRANS_TABLES
在該模式下,如果一個值不能插入到一個事物表中,則中斷當前的操作,對非事物表不做限制
NO_ZERO_IN_DATE
在嚴格模式下,不允許日期和月份為零
NO_ZERO_DATE
設置該值,mysql數據庫不允許插入零日期,插入零日期會拋出錯誤而不是警告。
ERROR_FOR_DIVISION_BY_ZERO
在INSERT或UPDATE過程中,如果數據被零除,則產生錯誤而非警告。如 果未給出該模式,那么數據被零除時MySQL返回NULL
NO_AUTO_CREATE_USER
禁止GRANT創建密碼為空的用戶
NO_ENGINE_SUBSTITUTION
如果需要的存儲引擎被禁用或未編譯,那么拋出錯誤。不設置此值時,用默認的存儲引擎替代,並拋出一個異常
PIPES_AS_CONCAT
將"||"視為字符串的連接操作符而非或運算符,這和Oracle數據庫是一樣的,也和字符串的拼接函數Concat相類似
ANSI_QUOTES
啟用ANSI_QUOTES后,不能用雙引號來引用字符串,因為它被解釋為識別符
HIGH_NOT_PRECEDENCE
NOT運算符的優先級使得諸如 NOT a BETWEEN b AND c 之類的表達式被解析為NOT(a BETWEEN b AND c)
mysql> SET sql_mode = '';
mysql> SELECT NOT 1 BETWEEN -5 AND 5;
-> 0
mysql> SET sql_mode = 'HIGH_NOT_PRECEDENCE';
mysql> SELECT NOT 1 BETWEEN -5 AND 5;
-> 1
IGNORE_SPACE
允許函數名和括號【(】之間的空格。這會導致內置函數名被視為保留字。
IGNORE_SPACE SQL模式適用於內置函數,而不適用於用戶定義的函數或存儲函數。 無論是否啟用IGNORE_SPACE,始終允許在UDF或存儲的函數名后面包含空格。
mysql> CREATE TABLE count (i INT);
ERROR 1064 (42000): You have an error in your SQL syntax
mysql> CREATE TABLE `count` (i INT);
Query OK, 0 rows affected (0.00 sec)