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

猜你喜欢

转载自blog.csdn.net/j_ychen/article/details/103030713
今日推荐