MySQL中的SQL_MODE模式

一、引言

最近开发的项目上线,当然了,在开发环境和测试环境都是测试过的。由于是新项目,所以线上环境的部署得从0开始。

当把所有的环境都搭建成功后,项目运行起来后,发现MySQL老是报一些语法错误,后来发现是每个环境的MySQLSQL_MODE模式不同。下面就来学习一下SQL_MODE吧。

二、SQL_MODE

1、何为SQL_MODE?

SQL_MODEMySQL中的一个系统变量(variable),可由多个MODE组成,每个MODE控制一种行为,如是否允许除数为0,日期中是否允许’0000-00-00’值。

2、为什么需要关注SQL_MODE呢?

下面来看三个简单的Demo,MySQL 版本为 5.6。

(1)、实际存储值与插入值不符
mysql> create table t1(c1 datetime);
Query OK, 0 rows affected (0.16 sec)

mysql> insert into t1 values('2019-02-29');
Query OK, 1 row affected, 1 warning (0.01 sec)

mysql> select * from t1;
+---------------------+
| c1                  |
+---------------------+
| 0000-00-00 00:00:00 |
+---------------------+
1 row in set (0.00 sec)
(2)、DDL导致原列内容丢失
mysql> create table t2(c1 varchar(10));
Query OK, 0 rows affected (0.06 sec)

mysql> insert into t2 values('a'),('b'),('c');
Query OK, 3 rows affected (0.01 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from t2;
+------+
| c1   |
+------+
| a    |
| b    |
| c    |
+------+
3 rows in set (0.00 sec)

mysql> alter table t2 modify column c1 int;
Query OK, 3 rows affected, 3 warnings (0.05 sec)
Records: 3  Duplicates: 0  Warnings: 3

mysql> show warnings;
+---------+------+-------------------------------------------------------+
| Level   | Code | Message                                               |
+---------+------+-------------------------------------------------------+
| Warning | 1366 | Incorrect integer value: 'a' for column 'c1' at row 1 |
| Warning | 1366 | Incorrect integer value: 'b' for column 'c1' at row 2 |
| Warning | 1366 | Incorrect integer value: 'c' for column 'c1' at row 3 |
+---------+------+-------------------------------------------------------+
3 rows in set (0.00 sec)

mysql> select * from t2;
+------+
| c1   |
+------+
|    0 |
|    0 |
|    0 |
+------+
3 rows in set (0.00 sec)
(3)、显式指定列和不显式指定的处理逻辑不一样
mysql> create table t3(id int not null,c1 varchar(10));
Query OK, 0 rows affected (0.05 sec)

mysql> insert into t3 values(null,'a');
ERROR 1048 (23000): Column 'id' cannot be null

mysql> insert into t3(c1) values('a');
Query OK, 1 row affected, 1 warning (0.00 sec)

mysql> show warnings;
+---------+------+-----------------------------------------+
| Level   | Code | Message                                 |
+---------+------+-----------------------------------------+
| Warning | 1364 | Field 'id' doesn't have a default value |
+---------+------+-----------------------------------------+
1 row in set (0.00 sec)

mysql> select * from t3;
+----+------+
| id | c1   |
+----+------+
|  0 | a    |
+----+------+
1 row in set (0.00 sec)
3、SQL_MODE的非严格模式

为什么会出现上面这三种情况呢?这个就与SQL_MODE有关。

MySQL 5.6中, SQL_MODE的默认值为"NO_ENGINE_SUBSTITUTION",非严格模式。

在这种模式下,在进行数据变更操作时,如果涉及的列中存在无效值(如日期不存在,数据类型不对,数据溢出),只会提示"Warning",并不会报错。

那么如何规避上述问题呢?答案是需开启SQL_MODE的严格模式

4、SQL_MODE的严格模式

所谓严格模式,即SQL_MODE中开启了STRICT_ALL_TABLESSTRICT_TRANS_TABLES

还是上面的Demo,看看严格模式下,MySQL的处理逻辑。

mysql> set session sql_mode='STRICT_TRANS_TABLES';
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t1 values('2019-02-29');
ERROR 1292 (22007): Incorrect datetime value: '2019-02-29' for column 'c1' at row 1

mysql> alter table t2 modify column c1 int;
ERROR 1366 (HY000): Incorrect integer value: 'a' for column 'c1' at row 1

mysql> insert into t3(c1) values('a');
ERROR 1364 (HY000): Field 'id' doesn't have a default value

同样的SQL,在严格模式下,直接提示"ERROR",而不是"Warning"。

5、STRICT_ALL_TABLES与STRICT_TRANS_TABLES的区别

同是严格模式,那STRICT_ALL_TABLESSTRICT_TRANS_TABLES有什么区别呢?

STRICT_TRANS_TABLES只对事务表开启严格模式,STRICT_ALL_TABLES是对所有表开启严格模式,不仅仅是事务表,还包括非事务表。

看下面这个测试,对myisam表插入3条数据,其中,第3条数据是空字符串,与定义的int类型不匹配。

mysql> create table t (c1 int) engine=myisam;
Query OK, 0 rows affected (0.00 sec)

mysql> set session sql_mode='STRICT_TRANS_TABLES';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> insert into t values (1),(2),('');
Query OK, 3 rows affected, 1 warning (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 1

mysql> show warnings;
+---------+------+------------------------------------------------------+
| Level   | Code | Message                                              |
+---------+------+------------------------------------------------------+
| Warning | 1366 | Incorrect integer value: '' for column 'c1' at row 3 |
+---------+------+------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select * from t;
+------+
| c1   |
+------+
|    1 |
|    2 |
|    0 |
+------+
3 rows in set (0.00 sec)

mysql> set session sql_mode='STRICT_ALL_TABLES';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> insert into t values (1),(2),('');
ERROR 1366 (HY000): Incorrect integer value: '' for column 'c1' at row 3

可以看到,在表为myisam存储引擎的情况下,只有开启STRICT_ALL_TABLES才会报错。

6、不同版本默认的SQL_MODE

MySQL 5.5:空
MySQL 5.6:

NO_ENGINE_SUBSTITUTION

MySQL 5.7:

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 8.0:

ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,
NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
7、如何修改SQL_MODE

SQL_MODE既可在全局级别修改,又可在会话级别修改。可指定多个MODEMODE之间用逗号隔开。

全局级别

set global sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES';

会话级别

set session sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES';
8、SQL_MODE的完整列表
(1)、ALLOW_INVALID_DATES

在严格模式下,对于日期的检测较为严格,其必须有效。若开启该MODE,对于month和day的检测会相对宽松。其中,month只需在1-12之间,day只需在1-31之间,而不管其是否有效,如下面的’2004-02-31’。

mysql> create table t (c1 datetime);
Query OK, 0 rows affected (0.21 sec)

mysql> set session sql_mode='STRICT_TRANS_TABLES';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> insert into t values('2004-02-31');
ERROR 1292 (22007): Incorrect datetime value: '2004-02-31' for column 'c1' at row 1

mysql> set session sql_mode='STRICT_TRANS_TABLES,ALLOW_INVALID_DATES';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> insert into t values('2004-02-31');
Query OK, 1 row affected (0.01 sec)

mysql> select * from t;
+---------------------+
| c1                  |
+---------------------+
| 2004-02-31 00:00:00 |
+---------------------+
1 row in set (0.00 sec)

注意,该MODE只适用于DATEDATETIME,不适用于TIMESTAMP

(2)、ANSI_QUOTES

MySQL中,对于关键字和保留字,是不允许用做表名和字段名的。如果一定要使用,必须使用反引号("`")进行转义。

mysql> create table order (id int);
ERROR 1064 (42000): You have an error in your SQL syntax; 
check the manual that corresponds to your MySQL server version 
for the right syntax to use near 'order (id int)' at line 1

mysql> create table `order` (id int);
Query OK, 0 rows affected (0.12 sec)

若开启该MODE,则双引号,同反引号一样,可对关键字和保留字转义。

mysql> set session sql_mode='';
Query OK, 0 rows affected (0.00 sec)

mysql> create table "order" (c1 int);
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"order" (c1 int)' at line 1

mysql> set session sql_mode='ANSI_QUOTES';
Query OK, 0 rows affected (0.00 sec)

mysql> create table "order" (c1 int);
Query OK, 0 rows affected (0.17 sec)

需要注意的是,在开启该MODE的情况下,不能再用双引号来引字符串。

(3)、ERROR_FOR_DIVISION_BY_ZERO

MODE决定除数为0的处理逻辑,实际效果还取决于是否开启严格模式。

1)、开启严格模式,且开启该MODE,插入1/0,会直接报错。

mysql> create table t (c1 double);
Query OK, 0 rows affected (0.04 sec)

mysql> set session sql_mode='STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> insert into t values(1/0);
ERROR 1365 (22012): Division by 0

2)、只开启严格模式,不开启该MODE,允许1/0的插入,且不提示warning,1/0最后会转化为NULL。

mysql> set session sql_mode='STRICT_TRANS_TABLES';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> insert into t values(1/0);
Query OK, 1 row affected (0.07 sec)

mysql> select * from t;
+------+
| c1  |
+------+
| NULL |
+------+
1 row in set (0.00 sec)

3)、不开启严格模式,只开启该MODE,允许1/0的插入,但提示warning。

4)、不开启严格模式,也不开启该MODE,允许1/0的插入,且不提示warning,同2一样。

(4)、HIGH_NOT_PRECEDENCE

默认情况下,NOT的优先级低于比较运算符。但在某些低版本中,NOT的优先级高于比较运算符。

看看两者的区别。

mysql> set session sql_mode='';
Query OK, 0 rows affected (0.00 sec)

mysql> select not 1 < -1;
+------------+
| not 1 < -1 |
+------------+
|          1 |
+------------+
1 row in set (0.00 sec)

mysql> set session sql_mode='HIGH_NOT_PRECEDENCE';
Query OK, 0 rows affected (0.00 sec)

mysql> select not 1 < -1;
+------------+
| not 1 < -1 |
+------------+
|          0 |
+------------+
1 row in set (0.00 sec)

sql_mode为空的情况下, not 1 < -1相当于not (1 < -1),如果设置了'HIGH_ NOT_PRECEDENCE',则相当于(not 1) < -1。

(5)、IGNORE_SPACE

默认情况下,函数名和左括号(“(”)之间不允许存在空格。若开启该MODE,则允许。

mysql> set session sql_mode='';
Query OK, 0 rows affected (0.00 sec)

mysql> select count(*) from t;
+----------+
| count(*) |
+----------+
|        2 |
+----------+
1 row in set (0.00 sec)

mysql> select count (*) from t;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '*) from t' at line 1

mysql> set session sql_mode='IGNORE_SPACE';
Query OK, 0 rows affected (0.01 sec)

mysql> select count (*) from t;
+-----------+
| count (*) |
+-----------+
|         2 |
+-----------+
1 row in set (0.01 sec)
(6)、NO_AUTO_VALUE_ON_ZERO

默认情况下,在对自增主键插入NULL或0时,会自动生成下一个值。若开启该MODE,当插入0时,并不会自动生成下一个值。

如果表中自增主键列存在0值,在进行逻辑备份还原时,可能会导致数据不一致。所以mysqldump在生成备份数据之前,会自动开启该MODE,以避免数据不一致的情况。

mysql> create table t (id int auto_increment primary key);
Query OK, 0 rows affected (0.11 sec)

mysql> set session sql_mode='';
Query OK, 0 rows affected (0.01 sec)

mysql> insert into t values (0);
Query OK, 1 row affected (0.04 sec)

mysql> select * from t;
+----+
| id |
+----+
|  1 |
+----+
1 row in set (0.00 sec)

mysql> set session sql_mode='NO_AUTO_VALUE_ON_ZERO';
Query OK, 0 rows affected (0.02 sec)

mysql> insert into t values (0);
Query OK, 1 row affected (0.09 sec)

mysql> select * from t;
+----+
| id |
+----+
|  0 |
|  1 |
+----+
2 rows in set (0.00 sec)
(7)、NO_BACKSLASH_ESCAPES

默认情况下,反斜杠“\”会作为转义符,若开启该MODE,则反斜杠“\”会作为一个普通字符,而不是转义符。

mysql> set session sql_mode='';
Query OK, 0 rows affected (0.01 sec)

mysql> select '\\t';
+----+
| \t |
+----+
| \t |
+----+
1 row in set (0.00 sec)

mysql> set session sql_mode='NO_BACKSLASH_ESCAPES';
Query OK, 0 rows affected (0.00 sec)

mysql> select '\\t';
+-----+
| \\t |
+-----+
| \\t |
+-----+
1 row in set (0.00 sec)
(8)、NO_DIR_IN_CREATE

默认情况下,在创建表时,可以指定数据目录(DATA DIRECTORY)和索引目录(INDEX DIRECTORY),若开启该MODE,则会忽略这两个选项。在主从复制场景下,可在从库上开启该MODE。

mysql> set session sql_mode='';
Query OK, 0 rows affected (0.01 sec)

mysql> create table t (id int) data directory '/tmp/';
Query OK, 0 rows affected (0.15 sec)

mysql> show create table t\G
*************************** 1. row ***************************
      Table: t
Create Table: CREATE TABLE `t` (
  `id` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci DATA DIRECTORY='/tmp/'
1 row in set (0.00 sec)

mysql> set session sql_mode='NO_DIR_IN_CREATE';
Query OK, 0 rows affected (0.00 sec)

mysql> drop table t;
Query OK, 0 rows affected (0.11 sec)

mysql> create table t (id int) data directory '/tmp/';
Query OK, 0 rows affected, 1 warning (0.05 sec)

mysql> show create table t\G
*************************** 1. row ***************************
      Table: t
Create Table: CREATE TABLE `t` (
  `id` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
(9)、NO_ENGINE_SUBSTITUTION

==在开启该MODE的情况下,在创建表时,如果指定的存储引擎不存在或不支持,则会直接提示“ERROR”。==若不开启,则只会提示“Warning”,且使用默认的存储引擎。

mysql> set session sql_mode='';
Query OK, 0 rows affected (0.00 sec)

mysql> create table t (id int) engine=federated;
Query OK, 0 rows affected, 2 warnings (0.11 sec)

mysql> show warnings;
+---------+------+-------------------------------------------+
| Level   | Code | Message                                   |
+---------+------+-------------------------------------------+
| Warning | 1286 | Unknown storage engine 'federated'        |
| Warning | 1266 | Using storage engine InnoDB for table 't' |
+---------+------+-------------------------------------------+
2 rows in set (0.00 sec)
mysql> show create table t\G
*************************** 1. row ***************************
      Table: t
Create Table: CREATE TABLE `t` (
  `id` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.01 sec)

mysql> drop table t;
Query OK, 0 rows affected (0.11 sec)

mysql> set session sql_mode='NO_ENGINE_SUBSTITUTION';
Query OK, 0 rows affected (0.00 sec)

mysql> create table t (id int) engine=federated;
ERROR 1286 (42000): Unknown storage engine 'federated'
(10)、NO_UNSIGNED_SUBTRACTION

两个整数相减,如果其中一个数是无符号位,默认情况下,会产生一个无符号位的值,如果该值为负数,则会提示“ERROR”,如,

mysql> set session sql_mode='';
Query OK, 0 rows affected (0.00 sec)

mysql> select cast(0 as unsigned)-1;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(cast(0 as unsigned) - 1)'

若开启该MODE,则允许结果为负数。

mysql> set session sql_mode='NO_UNSIGNED_SUBTRACTION';
Query OK, 0 rows affected (0.00 sec)

mysql> select cast(0 as unsigned)-1;
+-----------------------+
| cast(0 as unsigned)-1 |
+-----------------------+
|                    -1 |
+-----------------------+
1 row in set (0.00 sec)
(11)、NO_ZERO_DATE

该MODE会影响’0000-00-00’的插入。实际效果还取决于是否开启严格模式。

1)、在开启严格模式,且同时开启该MODE,是不允许’0000-00-00’插入的。

mysql> set session sql_mode='STRICT_TRANS_TABLES,NO_ZERO_DATE';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> show warnings\G
*************************** 1. row ***************************
  Level: Warning
  Code: 3135
Message: 'NO_ZERO_DATE', 'NO_ZERO_IN_DATE' and 'ERROR_FOR_DIVISION_BY_ZERO' sql modes should be used with strict mode. They will be merged with strict mode in
a future release.1 row in set (0.00 sec)

mysql> insert into t values ('0000-00-00');
ERROR 1292 (22007): Incorrect datetime value: '0000-00-00' for column 'c1' at row 1

2)、 只开启严格模式,不开启该MODE,允许’0000-00-00’值的插入,且不提示warning。

mysql> set session sql_mode='STRICT_TRANS_TABLES';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> insert into t values ('0000-00-00');
Query OK, 1 row affected (0.04 sec)

3)、不开启严格模式,只开启该MODE,允许’0000-00-00’值的插入,但提示warning。

mysql> set session sql_mode='NO_ZERO_DATE';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> insert into t values ('0000-00-00');
Query OK, 1 row affected, 1 warning (0.05 sec)

mysql> show warnings;
+---------+------+---------------------------------------------+
| Level   | Code | Message                                     |
+---------+------+---------------------------------------------+
| Warning | 1264 | Out of range value for column 'c1' at row 1 |
+---------+------+---------------------------------------------+
1 row in set (0.01 sec)

4)、不开启严格模式,也不开启该MODE,允许’0000-00-00’值的插入,且不提示warning。

mysql> set session sql_mode='';
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t values ('0000-00-00');
Query OK, 1 row affected (0.03 sec)
(12)、NO_ZERO_IN_DATE

NO_ZERO_DATE类似,只不过NO_ZERO_DATE针对的是’0000-00-00’,而NO_ZERO_IN_DATE针对的是年不为0,但月或者日为0的日期,如,‘2010-00-01’ or ‘2010-01-00’。

实际效果也是取决于是否开启严格模式,同NO_ZERO_DATE一样。

(13)、ONLY_FULL_GROUP_BY

开启该MODE,则SELECT列表中只能出现分组列和聚合函数。在这里插入代码片

mysql> set session sql_mode='';
Query OK, 0 rows affected (0.00 sec)

mysql> select dept_no,emp_no,min(from_date) from dept_emp group by dept_no;
+---------+--------+----------------+
| dept_no | emp_no | min(from_date) |
+---------+--------+----------------+
| d001    |  10017 | 1985-01-01     |
| d002    |  10042 | 1985-01-01     |
| d003    |  10005 | 1985-01-01     |
| d004    |  10003 | 1985-01-01     |
| d005    |  10001 | 1985-01-01     |
| d006    |  10009 | 1985-01-01     |
| d007    |  10002 | 1985-01-01     |
| d008    |  10007 | 1985-01-01     |
| d009    |  10011 | 1985-01-01     |
+---------+--------+----------------+
9 rows in set (0.64 sec)

mysql> set session sql_mode='ONLY_FULL_GROUP_BY';
Query OK, 0 rows affected (0.00 sec)

mysql> select dept_no,emp_no,min(from_date) from dept_emp group by dept_no;
ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'employees.dept_emp.emp_no' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

如果不开启该MODE,则允许SELECT列表中出现任意列,但这些列的值并不是确定的。

(14)、PAD_CHAR_TO_FULL_LENGTH

在对CHAR字段进行存储时,在Compact格式下,会占用固定长度的字节。

如下面的c1列,定义为char(10),虽然’ab’只占用两个字节,但在Compact格式下,会占用10个字节,不足部分以空格填充。

在查询时,默认情况下,会剔除掉末尾的空格。若开启该MODE,则不会剔除,每次都会返回固定长度的字符。

mysql> create table t (c1 char(10));
Query OK, 0 rows affected (0.17 sec)

mysql> insert into t values('ab');
Query OK, 1 row affected (0.11 sec)

mysql> set session sql_mode='';
Query OK, 0 rows affected (0.00 sec)

mysql> select c1, hex(c1), char_length(c1) from t;
+------+---------+-----------------+
| c1   | hex(c1) | char_length(c1) |
+------+---------+-----------------+
| ab   | 6162    |               2 |
+------+---------+-----------------+
1 row in set (0.00 sec)

mysql> set session sql_mode='PAD_CHAR_TO_FULL_LENGTH';
Query OK, 0 rows affected (0.00 sec)

mysql> select c1, hex(c1), char_length(c1) from t;
+------------+----------------------+-----------------+
| c1         | hex(c1)              | char_length(c1) |
+------------+----------------------+-----------------+
| ab         | 61622020202020202020 |              10 |
+------------+----------------------+-----------------+
1 row in set (0.00 sec)
(15)、PIPES_AS_CONCAT

在Oracle中,连接字符串可用concat和管道符("||"),但concat只能连接两个字符串(MySQL中的concat可连接多个字符),局限性太大,如果要连接多个字符串,一般用的是管道符。

开启该MODE,即可将管道符作为连接符。

mysql> set session sql_mode='';
Query OK, 0 rows affected (0.00 sec)

mysql> select 'a'||'b';
+----------+
| 'a'||'b' |
+----------+
|        0 |
+----------+
1 row in set, 2 warnings (0.00 sec)

mysql> select concat('a','b');
+-----------------+
| concat('a','b') |
+-----------------+
| ab              |
+-----------------+
1 row in set (0.00 sec)

mysql> set session sql_mode='PIPES_AS_CONCAT';
Query OK, 0 rows affected (0.00 sec)

mysql> select 'a'||'b';
+----------+
| 'a'||'b' |
+----------+
| ab       |
+----------+
1 row in set (0.00 sec)
(16)、REAL_AS_FLOAT

在创建表时,数据类型可指定为real,默认情况下,其会转化为double,若开启该MODE,则会转化为float。

mysql> set session sql_mode='';
Query OK, 0 rows affected (0.00 sec)

mysql> create table t ( c1 real);
Query OK, 0 rows affected (0.12 sec)

mysql> show create table t\G
*************************** 1. row ***************************
       Table: t
Create Table: CREATE TABLE `t` (
  `c1` double DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

mysql> drop table t;
Query OK, 0 rows affected (0.04 sec)

mysql> set session sql_mode='REAL_AS_FLOAT';
Query OK, 0 rows affected (0.00 sec)

mysql> create table t ( c1 real);
Query OK, 0 rows affected (0.11 sec)

mysql> show create table t\G
*************************** 1. row ***************************
       Table: t
Create Table: CREATE TABLE `t` (
  `c1` float DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
(17)、STRICT_ALL_TABLES

对事务表开启严格模式。

(18)、STRICT_TRANS_TABLES

对所有表开启严格模式。

(19)、TIME_TRUNCATE_FRACTIONAL

在时间类型定义了小数秒的情况下,如果插入的位数大于指定的位数,默认情况下,会四舍五入,若开启了该MODE,则会直接truncate掉。

mysql> create table t (c1 int,c2 datetime(2));
Query OK, 0 rows affected (0.04 sec)

mysql> set session sql_mode='';
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t values(1,'2018-08-08 11:12:13.125');
Query OK, 1 row affected (0.06 sec)

mysql> select * from t;
+------+------------------------+
| c1   | c2                     |
+------+------------------------+
|    1 | 2018-08-08 11:12:13.13 |
+------+------------------------+
1 row in set (0.00 sec)

mysql> set session sql_mode='TIME_TRUNCATE_FRACTIONAL';
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t values(2,'2018-08-08 11:12:13.125');
Query OK, 1 row affected (0.06 sec)

mysql> select * from t;
+------+------------------------+
| c1   | c2                     |
+------+------------------------+
|    1 | 2018-08-08 11:12:13.13 |
|    2 | 2018-08-08 11:12:13.12 |
+------+------------------------+
2 rows in set (0.00 sec)
(20)、NO_AUTO_CREATE_USER

在MySQL 8.0之前,直接授权会隐式创建用户。

mysql> select host,user from mysql.user where user='u1';
Empty set (0.00 sec)

mysql> grant all on *.* to 'u1'@'%' identified by '123';
Query OK, 0 rows affected, 1 warning (0.12 sec)

mysql> show warnings;
+---------+------+------------------------------------------------------------------------------------------------------------------------------------+
| Level   | Code | Message                                                                                                                            |
+---------+------+------------------------------------------------------------------------------------------------------------------------------------+
| Warning | 1287 | Using GRANT for creating new user is deprecated and will be removed in future release. Create new user with CREATE USER statement. |
+---------+------+------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select host,user from mysql.user where user='u1';
+------+------+
| host | user |
+------+------+
| %    | u1   |
+------+------+
1 row in set (0.00 sec)

同样的grant语句,在MySQL 8.0中是会报错的。

mysql> grant all on *.* to 'u1'@'%' identified by '123';
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'identified by '123'' at line 1

在MySQL 8.0中,已不允许grant语句隐式创建用户,所以,该MODE在8.0中也不存在。
从字面上看,该MODE是禁止授权时隐式创建用户。但在实际测试过程中,发现其并不能禁止。

mysql> set session sql_mode='NO_AUTO_CREATE_USER';
Query OK, 0 rows affected (0.03 sec)

mysql> grant all on *.* to 'u1'@'%' identified by '123';
Query OK, 0 rows affected, 1 warning (0.00 sec)

其实,该MODE禁止的只是不带“identified by”子句的grant语句,对于带有“identified by”子句的grant语句,其并不会禁止。

mysql> drop user u1;
Query OK, 0 rows affected (0.00 sec)

mysql> set session sql_mode='NO_AUTO_CREATE_USER';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> grant all on *.* to 'u1'@'%';
ERROR 1133 (42000): Can't find any matching row in the user table

mysql> set session sql_mode='';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> grant all on *.* to 'u1'@'%';
Query OK, 0 rows affected, 1 warning (0.00 sec)
9、SQL_MODE的常见组合

在MySQL 5.7中,还可将SQL_MODE设置为ANSI, DB2, MAXDB, MSSQL, MYSQL323, MYSQL40, ORACLE, POSTGRESQL, TRADITIONAL。

其实,这些MODE只是上述MODE的一种组合,目的是为了和其它数据库兼容。

在MySQL 8.0中,只支持ANSI和TRADITIONAL这两种组合。

ANSI等同于

REAL_AS_FLOAT, PIPES_AS_CONCAT,ANSI_QUOTES, IGNORE_SPACE, ONLY_FULL_GROUP_BY。
mysql> set session sql_mode='ANSI';
Query OK, 0 rows affected (0.00 sec)

mysql> show session variables like 'sql_mode';
+---------------+--------------------------------------------------------------------------------+
| Variable_name | Value                                                                          |
+---------------+--------------------------------------------------------------------------------+
| sql_mode      | REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ONLY_FULL_GROUP_BY,ANSI |
+---------------+--------------------------------------------------------------------------------+
1 row in set (0.03 sec)

TRADITIONAL
等同于

STRICT_TRANS_TABLES, STRICT_ALL_TABLES,NO_ZERO_IN_DATE,
NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION。
mysql> set session sql_mode='TRADITIONAL';
Query OK, 0 rows affected (0.00 sec)

mysql> show session variables like 'sql_mode';
+---------------+----------------------------------------------------------------------------------------------------------------------------------+
| Variable_name | Value                                                                                                                            |
+---------------+----------------------------------------------------------------------------------------------------------------------------------+
| sql_mode      | STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_ENGINE_SUBSTITUTION |
+---------------+----------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)

三、小结

SQL_MODE在非严格模式下,会出现很多意料不到的结果。建议线上开启严格模式。但对于线上老的环境,如果一开始就运行在非严格模式下,切忌直接调整,毕竟两者的差异性还是相当巨大。

官方默认的SQL_MODE一直在发生变化,MySQL 5.5, 5.6, 5.7就不尽相同,但总体是趋严的,在对数据库进行升级时,其必须考虑默认的SQL_MODE是否需要调整。

在进行数据库迁移时,可通过调整SQL_MODE来兼容其它数据库的语法。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

止步前行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值