最近将测试环境的MySQL从5.0升级到了5.5,然后升级之后同事说有些insert SQL执行不了,一开始还以为是MySQL版本的问题,
后来查了下发现MySQL是因为MySQL有个sql mode设置,所以查看了下2个MySQL的sql mode设置,发现果然不一样
果然是不一样的呢,那就学习下MySQL的sql mode吧。
mysql5.0以上版本支持三种sql_mode模式:ANSI、TRADITIONAL和STRICT_TRANS_TABLES。
ANSI模式:宽松模式,对插入数据进行校验,如果不符合定义类型或长度,对数据类型调整或截断保存,报warning警告。
TRADITIONAL模式:严格模式,当向mysql数据库插入数据时,进行数据的严格校验,保证错误数据不能插入,报error错误。用于事物时,会进行事物的回滚。
STRICT_TRANS_TABLES模式:严格模式,进行数据的严格校验,错误数据不能插入,报error错误。
首先我们设置ANSI模式.
mysql> show variables like 'sql_mode';+---------------+--------------------------------------------+ | Variable_name | Value | +---------------+--------------------------------------------+ | sql_mode | STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION | +---------------+--------------------------------------------+ 1 row in set (0.00 sec) mysql> set @@sql_mode=ANSI; Query OK, 0 rows affected (0.00 sec) mysql> show variables like 'sql_mode'; +---------------+-------------------------------------------------------------+ | Variable_name | Value | +---------------+-------------------------------------------------------------+ | sql_mode | REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ANSI | +---------------+-------------------------------------------------------------+ 1 row in set (0.00 sec)
这是设置之后的sql_mode.然后我们建立表格插入数据.
mysql> create table tt(name varchar(4), pass varchar(4)); Query OK, 0 rows affected (0.09 sec) mysql> mysql> insert into tt values('aaaaa','aaaaa'),('bbbb','bbbb'); Query OK, 2 rows affected, 2 warnings (0.00 sec) Records: 2 Duplicates: 0 Warnings: 2 mysql> show warnings; +---------+------+-------------------------------------------+ | Level | Code | Message | +---------+------+-------------------------------------------+ | Warning | 1265 | Data truncated for column 'name' at row 1 | | Warning | 1265 | Data truncated for column 'pass' at row 1 | +---------+------+-------------------------------------------+ 2 rows in set (0.00 sec) mysql> select * from tt; +------+------+ | name | pass | +------+------+ | aaaa | aaaa | | bbbb | bbbb | +------+------+ 2 rows in set (0.00 sec)
这时候发现数据可以插入成功,只是产生了2条waring数据,数据也被截取过了。
上面的是插入对应相同列的数据,我们再试下只插入一列数据的情况
mysql> insert into tt(name) values('eeee'),('2eeee'); Query OK, 2 rows affected, 1 warning (0.01 sec) Records: 2 Duplicates: 0 Warnings: 1 mysql> show warnings; +---------+------+-------------------------------------------+ | Level | Code | Message | +---------+------+-------------------------------------------+ | Warning | 1265 | Data truncated for column 'name' at row 2 | +---------+------+-------------------------------------------+ 1 row in set (0.00 sec) mysql> select * from tt; +------+------+ | name | pass | +------+------+ | aaaa | aaaa | | bbbb | bbbb | | eeee | NULL | | 2eee | NULL | +------+------+ 4 rows in set (0.00 sec)
如果我们只设置了第一列name的value,而且另外一个列pass并没有设置为allow null的情况下,数据也是可以插入成功的。但是也有2条warning,而且数据也是被截取过的。
那接下来我们设置STRICT_TRANS_TABLES模式:
mysql> set @@sql_mode=STRICT_TRANS_TABLES; Query OK, 0 rows affected (0.00 sec) mysql> show variables like 'sql_mode'; +---------------+---------------------+ | Variable_name | Value | +---------------+---------------------+ | sql_mode | STRICT_TRANS_TABLES | +---------------+---------------------+ 1 row in set (0.00 sec)
重复我们刚刚插入数据的方式
mysql> insert into tt values('aaaaa','aaaaa'),('bbbb','bbbb'); ERROR 1406 (22001): Data too long for column 'name' at row 1
发现SQL直接报错了,提示数据太长而不能插入.
修改数据插入
mysql> insert into test values('aaaa','aaaa'),('bbbbb','bbbbb'); ERROR 1406 (22001): Data too long for column 'name' at row 2
mysql> select * from test; +------+------+ | name | pass | +------+------+ | aaaa | aaaa | | bbbb | bbbb | | eeee | NULL | | 2eee | NULL | +------+------+ 4 rows in set (0.00 sec)
发现数据都是插入不成功的。那我们再试试刚刚只插入1列数据的情况呢
mysql> insert test(name) value('wmq'); Query OK, 1 row affected (0.03 sec) mysql> select * from test; +------+------+-------+ | name | pass | value | +------+------+-------+ | test | | | | wmq | | | +------+------+-------+ 2 rows in set (0.00 sec)
也是可以插入成功的,所以我可以自动,sql mode跟字段是否匹配没有关系。但是我们查看table info的create SQL可以发现,有的MySQL客户端
在建立表的时候,会给字段默认加上defalut '' 这个值,例如:
CREATE TABLE `test1` ( `name` varchar(4) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
和
CREATE TABLE `test2` ( `name` varchar(4) NOT NULL DEFAULT '', ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
这是在我客户端和我同事的客户端上建表的create语句。有的客户端会让你选择default custom 有的会显示无默认值。但是我电脑的客户端就没有让你选择
default ''的这个选项。
这点以后一定要非常注意,最好是统一使用相同的MySQL客户端或者干脆禁用远程账户,统一使用PMA或者命令行,这里有点偏题了
最后我们设置TRADITIONAL:
mysql> set @@sql_mode = 'TRADITIONAL'; Query OK, 0 rows affected (0.00 sec) mysql> show 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_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION | +---------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
发现和STRICT_TRANS_TABLES一样