参考官方文档:
mysql可以为不同的客户端设置不同的sql_mode,并且每个应用能够设置他自己的会话级别的sql_mode。sql_mode会影响sql语法以及mysql显示数据的正确性。
When working with InnoDB tables, consider also the innodb_strict_mode system variable. It enables additional error checks for InnoDB tables.
官方文档建议:当使用innodb存储引擎表时,考虑使用innodb_strict_mode模式的sql_mode,它能增量额外的错误检测功能。
设置sql mode
mysql5.7默认的sql mode值如下:
root@(none) 05:48:26>show variables like "sql_mode"; +---------------+-------------------------------------------------------------------------------------------------------------------------------------------+ | Variable_name | Value | +---------------+-------------------------------------------------------------------------------------------------------------------------------------------+ | sql_mode | 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 | +---------------+-------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.02 sec) root@(none) 05:48:34>
在服务启动之前在配置文件中设置sql mode:
sql_mode="ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
或者在启动mysql的时候使用--sql-mode="value"来指定sql mode的值。
可以在命令行设置全局的sql mode,也可以设置会话级别的。
set global sql_mode="modes"; #全局的设置影响连接的每一个客户端, set session sql_mode="modes"; #会话级别的设置只影响当前的客户端,
(需要说明的是,在命令行的设置重启服务后会失效)
mysql支持三种级别的sql mode
- ANSI: 宽松模式,对插入数据进行校验,如果不符合定义类型或长度,对数据类型调整或截断保存,报warning警告。
- STRICT_TRANS_TABLES:
If a value could not be inserted as given into a transactional table, abort the statement. For a nontransactional table, abort the statement if the value occurs in a
single-row statement or the first row of a multiple-row statement.
#对于事务表,给的值不能插入则终止该语句。对于非事务表,若是单行语句或者多行语句的第一行则中断() - TRADITIONAL(traditional): mysql对插入的数据不会报警告而是直接给出错误。当使用的是事务表,发生错误时会直接回滚;若是非事务表则会造成数据的不一致的情况(错误前的数据已经插入)
mysql支持的所有模式
- ALLOW_INVALID_DATES: 字母意思允许不合法的数据。不对日期做全面的检查,仅仅检查月份是否在1~12之间,天数是否在1~31之间;这种模式可能是有用的对web应用来说去获取年,月,日在三个不同的字段并且准确存储用户的输入数值,没有验证数据的合法性。这种模式对date和datetime类型有作用,但是对timestamp类型不起作用,timestamp总是要合法的数据。当ALLOW_INVALID_DATES启用时,服务端要求年和月时合法的。如果strict模式禁用,不合法的数据如"2004-04-31"被存储为"0000-00-00"并且审查警告;若严格模式启用则会生成错误。(最后这一句我确定没有翻译错,但是测试的时候,数据时原样插入的,没有转换为0000-00-00)
测试结果
root@testdb 06:19:08>create table tb2(l1 date); Query OK, 0 rows affected (0.22 sec) root@testdb 06:23:23>set session sql_mode="ALLOW_INVALID_DATES"; Query OK, 0 rows affected, 1 warning (0.00 sec) root@testdb 06:23:41>insert into tb2 values("2012-02-30"); Query OK, 1 row affected (0.00 sec) root@testdb 06:23:45>select * from tb2; +------------+ | l1 | +------------+ | 2012-02-30 | +------------+ 1 row in set (0.00 sec) root@testdb 06:23:56>insert into tb2 values("2012-04-31"); Query OK, 1 row affected (0.09 sec) root@testdb 06:24:44>select * from tb2; +------------+ | l1 | +------------+ | 2012-02-30 | | 2012-04-31 | +------------+ 2 rows in set (0.00 sec) root@testdb 06:24:46>set session sql_mode="TRADITIONAL"; Query OK, 0 rows affected, 1 warning (0.00 sec) root@testdb 06:26:26>insert into tb2 values("2012-06-31"); ERROR 1292 (22007): Incorrect date value: '2012-06-31' for column 'l1' at row 1 root@testdb 06:26:38>