MYSQL 5.7 SQL_MODE

Server SQL Modes

MySQL服務器可以在不同的SQL模式下運行,並且可以根據sql_mode系統變量的值將這些模式不同地應用於不同的客戶端。DBA可以設置全局SQL模式以匹配站點服務器操作要求,並且每個應用程序可以將其會話SQL模式設置爲自己的要求。

模式會影響MySQL支持的SQL語法以及它執行的數據驗證檢查。這使得在不同環境中使用MySQL以及將MySQL與其他數據庫服務器一起使用更加容易。

設置SQL_MODE

在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中的默認SQL模式: ONLY_FULL_GROUP_BY和 STRICT_TRANS_TABLES模式已添加到MySQL 5.7.5中。該NO_AUTO_CREATE_USER模式是在MySQL 5.7.7中添加的。ERROR_FOR_DIVISION_BY_ZERO, NO_ZERO_DATE以及 NO_ZERO_IN_DATE模式是在MySQL 5.7.8添加。

要在運行時更改SQL模式,請使用以下SET 語句設置全局或會話 系統變量:

SET GLOBAL sql_mode = ‘modes’;
SET SESSION sql_mode = ‘modes’;

設置GLOBAL變量需要 SUPER特權,並且會影響此後連接的所有客戶端的操作。
設置SESSION變量僅影響當前客戶端。每個客戶端可以隨時更改其會話 sql_mode值。

要確定當前的全局或會話 sql_mode設置,請選擇其值:

SELECT @@GLOBAL.sql_mode;
SELECT @@SESSION.sql_mode;

SQL模式和用戶定義的分區中需要注意的問題:
在創建數據並將數據插入分區表後更改服務器SQL模式可能會導致此類表的行爲發生重大更改,並可能導致數據丟失或損壞。強烈建議在使用用戶定義的分區創建表後,不要更改SQL模式。
複製分區表時,主服務器和從服務器上不同的SQL模式也會導致問題。爲了獲得最佳結果,您應該始終在主服務器和從服務器上使用相同的服務器SQL模式。

最重要的SQL模式

  • ANSI

此模式更改語法和行爲,使其更接近標準SQL。
相當於 REAL_AS_FLOAT, PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE,和(從MySQL 5.7.5的) ONLY_FULL_GROUP_BY。這種模式遇到異常時傾向於警告而不是立刻返回錯誤。

  • STRICT_TRANS_TABLES

這種模式適用於事務表和非事務表,它嚴格模式,不允許非法日期,也不允許超過字段長度的值插入字段中,對於插入不正確的值給出錯誤而不是警告。mysql 5.7版本後添加到默認的sql mode 中。

  • TRADITIONAL

在MySQL 5.7.4之前,以及在MySQL 5.7.8或更高版本, TRADITIONAL就相當於STRICT_TRANS_TABLES, STRICT_ALL_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, NO_AUTO_CREATE_USER,和 NO_ENGINE_SUBSTITUTION的組合模式,所以它也是嚴格模式,對於插入不正確的值會出給出錯誤而不是警告。可以應用在事務表非事務表,用在事務表時,只要出現錯誤就會立即回滾。

SQL模式列表

  • ONLY_FULL_GROUP_BY

在HAVING條件或ORDER BY列表引用GROUP BY未在該子句中命名且在功能上不依賴於(由其唯一確定) GROUP BY列的未聚合的列,對於這些查詢拒絕查詢。
從MySQL 5.7.5開始,默認的SQL模式包括 ONLY_FULL_GROUP_BY。(在5.7.5之前,MySQL不會檢測功能依賴關係,並且ONLY_FULL_GROUP_BY默認情況下未啓用)
 
摘自官網:https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
 
MySQL 5.7.5及更高版本還允許在啓用ONLY_FULL_GROUP_BY模式時在GROUP BY子句中不命名的非聚合列,前提是此列僅限於一個值,如下例所示:
mysql> CREATE TABLE mytable (
-> id INT UNSIGNED NOT NULL PRIMARY KEY,
-> a VARCHAR(10),
-> b INT
-> );
 
mysql> INSERT INTO mytable
-> VALUES (1, ‘abc’, 1000),
-> (2, ‘abc’, 2000),
-> (3, ‘def’, 4000);
 
mysql> SET SESSION sql_mode = sys.list_add(@@session.sql_mode, ‘ONLY_FULL_GROUP_BY’);
 
mysql> SELECT a, SUM(b) FROM mytable WHERE a = ‘abc’;
±-----±-------+
| a | SUM(b) |
±-----±-------+
| abc | 3000 |
±-----±-------+

當啓用ONLY_FULL_GROUP_BY模式時,也可以在select列表中有多個非聚合列。在這種情況下,每一個這樣的列必須限制爲一個值,並且所有這樣的限制條件必須由邏輯連接and,如下所示:
mysql> DROP TABLE IF EXISTS mytable;
 
mysql> CREATE TABLE mytable (
-> id INT UNSIGNED NOT NULL PRIMARY KEY,
-> a VARCHAR(10),
-> b VARCHAR(10),
-> c INT
-> );
 
mysql> INSERT INTO mytable
-> VALUES (1, ‘abc’, ‘qrs’, 1000),
-> (2, ‘abc’, ‘tuv’, 2000),
-> (3, ‘def’, ‘qrs’, 4000),
-> (4, ‘def’, ‘tuv’, 8000),
-> (5, ‘abc’, ‘qrs’, 16000),
-> (6, ‘def’, ‘tuv’, 32000);
 
mysql> SELECT @@session.sql_mode;
±--------------------------------------------------------------+
| @@session.sql_mode |
±--------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION |
±--------------------------------------------------------------+
 
mysql> SELECT a, b, SUM© FROM mytable
-> WHERE a = ‘abc’ AND b = ‘qrs’;
±-----±-----±-------+
| a | b | SUM© |
±-----±-----±-------+
| abc | qrs | 17000 |
±-----±-----±-------+

如果ONLY_FULL_GROUP_BY被禁用,則對標準SQL使用的MySQL擴展 GROUP BY允許select列表時, HAVING條件或ORDER BY列表引用未聚合的列,即使這些列在功能上不依賴於GROUP BY列。這導致MySQL接受前面的查詢。在這種情況下,服務器可以從每個組中自由選擇任何值,因此,除非它們相同,否則選擇的值是不確定的,這可能不是您想要的。此外,不能通過添加ORDER BY子句來影響從每個組中選擇值。結果集排序發生在選擇值之後,排序方式不影響服務器選擇的每個組中的哪個值。如果您知道由於數據的某些屬性,對於每個組,每個未在GROUP BY中命名的未聚合列中的所有值都是相同的,則禁用ONLY_FULL_GROUP_BY非常有用。
 
通過使用ANY_VALUE()來引用未聚合的列,您可以在不禁用ONLY_FULL_GROUP_BY的情況下實現相同的效果。

此查詢ONLY_FULL_GROUP_BY啓用後可能無效, 因爲 子句中address未命名選擇列表中的非聚合列GROUP BY:
SELECT name, address, MAX(age) FROM t GROUP BY name;
 
如果name是t的主鍵或是唯一的非空列,則查詢有效。在這種情況下,MySQL會認識到所選列在功能上取決於分組列。例如,如果name是主鍵,則其值確定的值,address因爲每個組只有主鍵的一個值,因此只有一行。結果,address在組中選擇值時不會出現隨機性 ,也不需要拒絕查詢。
如果name不是t的主鍵或唯一的非空列,則查詢無效。在這種情況下,無法推斷出功能依賴性,並且會發生錯誤:
 
mysql> SELECT name, address, MAX(age) FROM t GROUP BY name;
ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP
BY clause and contains nonaggregated column ‘mydb.t.address’ which
is not functionally dependent on columns in GROUP BY clause; this
is incompatible with sql_mode=only_full_group_by
 
如果您知道,對於給定的數據集, 每個name值實際上唯一地確定該address值,address 實際上在功能上取決於 name。要告訴MySQL接受查詢,可以使用以下ANY_VALUE()函數:
SELECT name, ANY_VALUE(address), MAX(age) FROM t GROUP BY name;
或者,禁用 ONLY_FULL_GROUP_BY。
 
如果查詢中聚合函數和沒有GROUP BY條款,也不能有非聚合列在select列表,HAVING條件,或 ORDER BY清單, ONLY_FULL_GROUP_BY啓用:
mysql> SELECT name, MAX(age) FROM t;
ERROR 1140 (42000): In aggregated query without GROUP BY, expression
#1 of SELECT list contains nonaggregated column ‘mydb.t.name’; this
is incompatible with sql_mode=only_full_group_by
如果沒有GROUP BY,則只有一個組,並且不確定name爲該組選擇哪個值。如果MySQL選擇name值不重要,也可以在這裏使用ANY_VALUE():
SELECT ANY_VALUE(name), MAX(age) FROM t;

在MySQL 5.7.5和更高版本中,ONLY_FULL_GROUP_BY 還會影響使用DISTINCT和的查詢的處理 ORDER BY。考慮表的情況下,t有三列c1,c2以及 c3包含這些行:
c1 c2 c3
1 2 A
3 4 B
1 2 C
 
假設我們執行以下查詢,期望結果按以下順序排序c3:
SELECT DISTINCT c1, c2 FROM t ORDER BY c3;
 
爲了獲得結果,必須首先消除重複項。但要這樣做,我們應該保留第一排還是第三排?這種任意的選擇會影響c3的保留值,而c3的保留值又會影響排序,並使排序變得任意。爲了防止此問題,如果任何ORDER BY表達式不滿足以下條件之一,則具有DISTINCT和ORDER BY的查詢將被拒絕爲無效:

  • 該表達式等於選擇列表中的一個
  • 表達式引用的並屬於查詢的所選表的所有列都是選擇列表的元素
     

對標準SQL的另一個MySQL擴展允許在HAVING子句中引用選擇列表中的別名表達式。例如,以下查詢返回 name在表中僅出現一次的值 orders:
SELECT name, COUNT(name) FROM orders
GROUP BY name
HAVING COUNT(name) = 1;
 
MySQL擴展允許在HAVING子句中爲聚合列使用別名 :
SELECT name, COUNT(name) AS c FROM orders
GROUP BY name
HAVING c = 1;
 
標準SQL在GROUP BY子句中僅允許使用列表達式,因此這樣的語句是無效的,因爲FLOOR(value/100)它是非列表達式:
SELECT id, FLOOR(value/100)
FROM tbl_name
GROUP BY id, FLOOR(value/100);
MySQL擴展了標準SQL,允許GROUP BY子句中使用非列表達式, 並認爲前面的語句有效。

標準SQL也不允許在GROUP BY子句中使用別名。MySQL擴展了標準SQL以允許使用別名,因此另一種編寫查詢的方法如下:
SELECT id, FLOOR(value/100) AS val
FROM tbl_name
GROUP BY id, val;
別名val在GROUP BY子句中被視爲列表達式。
 
在GROUP BY子句中存在非列表達式時,MySQL會識別該表達式與選擇列表中的表達式之間的相等性。這意味着在ONLY_FULL_GROUP_BY啓用S​​QL模式的情況下,包含的查詢GROUP BY id, FLOOR(value/100)是有效的,因爲相同的 FLOOR()表達式出現在選擇列表中。但是,MySQL不會嘗試識別對非GROUP BY列表達式的功能依賴性,因此ONLY_FULL_GROUP_BY,即使第三個選擇的表達式是該id列和子句中的 FLOOR()表達式的簡單公式,以下查詢在啓用後也是無效 的 GROUP BY:
SELECT id, FLOOR(value/100), id+FLOOR(value/100)
FROM tbl_name
GROUP BY id, FLOOR(value/100);
 
解決方法是使用派生表:
SELECT id, F, id+F
FROM
(SELECT id, FLOOR(value/100) AS F
FROM tbl_name
GROUP BY id, FLOOR(value/100)) AS dt;

  • STRICT_TRANS_TABLES

如果不能按照給定值插入事務表中,中止該語句。對於非事務表,如果該值出現在單行語句或多行語句的第一行中,則中止該語句。
該選項針對事務性存儲引擎生效,對於非事務性存儲引擎無效,該選項表示開啓strict sql模式。在strict sql模式下,在INSERT或者UPDATE語句中,插入或者更新了某個不符合規定的字段值,則會直接報錯中斷操作。
STRICT_TRANS_TABLES 包括的影響 ERROR_FOR_DIVISION_BY_ZERO, NO_ZERO_DATE和 NO_ZERO_IN_DATE模式

  • NO_ZERO_IN_DATE

該NO_ZERO_IN_DATE模式影響服務器是否允許年份部分非零但月份或日期部分爲0的日期。(此模式影響日期,例如’2010-00-01’或 ‘2010-01-00’,但不 影響日期’0000-00-00’。要控制服務器是否允許’0000-00-00’使用此 NO_ZERO_DATE模式。)NO_ZERO_IN_DATE 的效果還取決於是否啓用了strict sql 模式。

  • 如果未啓用此模式,則允許零部分的日期,並且插入不會產生任何警告。
  • 如果啓用此模式,則將零份日期插入爲’0000-00-00’併產生警告。
  • 如果啓用了此模式和嚴格模式,則除非IGNORE同時指定,否則不允許使用零份日期,並且插入會產生錯誤。對於INSERT IGNORE和 UPDATE IGNORE,將零部分的日期作爲插入’0000-00-00’併產生警告。
     

NO_ZERO_IN_DATE不推薦使用。 NO_ZERO_IN_DATE不是嚴格模式的一部分,但應與嚴格模式結合使用,並且默認情況下處於啓用狀態。如果NO_ZERO_IN_DATE在未啓用嚴格模式的情況下啓用,則會出現警告, 反之亦然。
因爲NO_ZERO_IN_DATE不推薦使用,所以它將在將來的MySQL版本中作爲單獨的模式名稱刪除,並且其影響包括在strict sql 模式的影響中。

  • NO_ZERO_DATE

該NO_ZERO_DATE模式影響服務器是否允許將其 '0000-00-00’作爲有效日期。其效果還取決於是否啓用了嚴格的SQL模式。

  • 如果未啓用此模式, '0000-00-00’則允許並且插入不會產生警告。
  • 如果啓用此模式,‘0000-00-00’ 則允許並插入將產生警告。
  • 如果啓用了此模式和嚴格模式, '0000-00-00’則不允許這樣做,並且插入也會產生錯誤,除非 IGNORE同樣給出。對於 INSERT IGNORE和UPDATE IGNORE,'0000-00-00’允許並插入產生警告。
     

NO_ZERO_DATE不推薦使用。NO_ZERO_DATE 不是嚴格模式的一部分,但應與嚴格模式結合使用,並且默認情況下處於啓用狀態。如果NO_ZERO_DATE在未啓用嚴格模式的情況下啓用了,則會出現警告, 反之亦然。
因爲NO_ZERO_DATE不推薦使用,所以它將在將來的MySQL版本中作爲單獨的模式名稱刪除,並且其影響包括在strict sql 模式的影響中。

  • ERROR_FOR_DIVISION_BY_ZERO

該 ERROR_FOR_DIVISION_BY_ZERO 模式影響除以零的處理,包括MOD(N,0) 。對於數據更改操作(插入、更新),其影響還取決於是否啓用了strict sql 模式。

  • 如果未啓用此模式,則除以零將插入 NULL並且不產生警告。
  • 如果啓用此模式,則除以零將插入 NULL併產生警告。
  • 如果啓用了此模式和嚴格模式,則除非另外IGNORE 指定,否則除以零會產生錯誤。對於INSERT IGNORE 和UPDATE IGNORE,除以零將插入NULL併產生警告。
     

對於SELECT,除以零則返回NULL。ERROR_FOR_DIVISION_BY_ZERO 無論是否啓用嚴格模式,啓用都會導致產生警告。
ERROR_FOR_DIVISION_BY_ZERO 不推薦使用。 ERROR_FOR_DIVISION_BY_ZERO 不是嚴格模式的一部分,但應與嚴格模式結合使用,並且默認情況下處於啓用狀態。如果ERROR_FOR_DIVISION_BY_ZERO 在未啓用嚴格模式的情況下啓用了警告, 反之亦然。
因爲 ERROR_FOR_DIVISION_BY_ZERO 不推薦使用,所以它將在將來的MySQL版本中作爲單獨的模式名稱刪除,並且其影響包括在嚴格SQL模式的影響中。

  • NO_AUTO_CREATE_USER

除非指定了身份驗證信息,否則禁止GRANT語句自動創建新用戶帳戶。如果它不這樣做的話,語句必須使用IDENTIFIED BY指定非空密碼,或使用來指定身份驗證插件IDENTIFIED WITH。
 
最好使用create user而不是使用grant創建MySQL帳戶。NO_AUTO_CREATE_USER已啓用,默認的SQL模式包括NO_AUTO_CREATE_USER。將sql_mode 更改爲 NO_AUTO_CREATE_USER模式,狀態的分配會產生警告,但將sql_mode設置爲默認分配除外。 NO_AUTO_CREATE_USER將在將來的MySQL版本中刪除,屆時將始終啓用其效果(GRANT不會創建帳戶)。
 
以前,在NO_AUTO_CREATE_USER不贊成使用之前 ,不啓用它的一個原因是它不是複製安全的。現在,它可以啓用和進行復制,安全的用戶管理CREATE USER IF NOT EXISTS,DROP USER IF EXISTS和ALTER USER IF EXISTS而不是 GRANT。當從機的授權與主機的授權不同時,這些語句可實現安全複製

  • NO_ENGINE_SUBSTITUTION

當諸如CREATE TABLE或ALTER TABLE指定的存儲引擎被禁用或未編譯時,控制默認存儲引擎的自動替換。
默認情況下 NO_ENGINE_SUBSTITUTION啓用。
由於存儲引擎可以在運行時插入,因此無法使用的引擎將以相同的方式處理:
禁用NO_ENGINE_SUBSTITUTION時,對於CREATE TABLE,將使用默認引擎,如果所需引擎不可用,則會出現警告。對於ALTER TABLE,將出現一個警告,並且表不會被更改。
如果未啓用任何引擎替換,則會發生錯誤,並且如果所需引擎不可用,則不會創建或更改表。

MySQL 5.7中的SQL模式更改

在MYSQL 5.7.22,這些SQL模式被取消,並將在MySQL的未來版本中刪除: DB2, MAXDB, MSSQL, MYSQL323, MYSQL40, ORACLE, POSTGRESQL, NO_FIELD_OPTIONS, NO_KEY_OPTIONS, NO_TABLE_OPTIONS。

在MySQL 5.7中, ONLY_FULL_GROUP_BY默認情況下啓用SQL模式,因爲GROUP BY 處理變得更加複雜以包括對功能依賴項的檢測。但是,如果您發現ONLY_FULL_GROUP_BY啓用該 功能會導致對現有應用程序的查詢被拒絕,則以下任一操作都應恢復操作:

  • 如果可以修改有問題的查詢,請這樣做,以使未聚合的列在功能上依賴於GROUP BY列,或者使用ANY_VALUE()引用未聚合的列。
  • 如果無法修改有問題的查詢(例如,如果該查詢是由第三方應用程序生成的),請在服務器啓動時將sql_mode系統變量設置爲不啓用ONLY_FULL_GROUP_BY。

在MySQL 5.7中, ERROR_FOR_DIVISION_BY_ZERO, NO_ZERO_DATE,和 NO_ZERO_IN_DATESQL模式已被棄用。長期計劃是將這三種模式包含在嚴格的SQL模式中,並在將來的MySQL版本中將它們作爲顯式模式刪除。爲了使MySQL 5.7與MySQL 5.6嚴格模式兼容,併爲修改受影響的應用程序提供額外的時間,適用以下行爲:

  • 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是默認啓用的。

通過上述更改,默認情況下仍會啓用更嚴格的數據檢查,但是可以在當前需要或必須這樣做的環境中禁用各個模式。

摘自官網:https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章