MySQL 5.7:聊聊sql_mode


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)

參考資料:https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM