导语

  mysql服务器可以在不同的sql_mode模式下运行,并且可以根据sql_mode系统变量的值,为不同的客户机应用不同的模式。sql_mode会影响mysql支持的sql语法,并且会执行数据验证检查,那不同的mysql_mode是如何影响支持的sql语法和数据类型检验的呢?本文将给大家系统总结分析说明这个问题,以及在sql_mode为空的时候,测试需要注意的测试点。
目录

 一、经典的“测试缺陷”

 二、Server SQL Modes 介绍
2.1 sql_mode概念
2.2 七种常见的sql_mode
ONLY_FULL_GROUP_BY
ANSI_QUOTES
PIPES_AS_CONCAT
NO_AUTO_CREATE_USER
NO_ZERO_DATE
NO_ZERO_IN_DATE
STRICT_TRANS_TABLES
2.3 三个重要且常用的sql_mode模式
ANSI
STRICT_TRANS_TABLES
TRADITIONA 三、测试策略
3.1 小结:宽松,严格模式下常见sql实例影响和结果对比
3.2 宽松模式下测试应该注意哪些地方?

一:经典的“测试缺陷”

某日版本一如常规的发布上线,灰度过程中开发传来了一个“噩耗”,程序在操作数据库时有bug。

开发:第一笔数据插入都正常,第二笔数据插入就报重入了,报主键冲突,而实际这两笔数据的主键应该是不一样的,后面发现第一笔数据插入时的主键值是错误导致的。

测试:怎么会,在日志里打印的每条sql语句我都是有检查的,测试环境插入多少遍都从没报过这样的错误,而且每笔数据插入完成后,DB表的主键,业务关键字段也都是有检查的,当时我测试的记录还在,主键肯定是插入对的。

开发:在日志里查看sql语句的主键值是对的,但是插入db的主键是被截断的错误的。截断入库还不报error错误,是因为线上mysql的sql_mode为空是宽松模式,导致数据插入时被截断,也不报error错误,业务层会以为是插入成功业务继续,从而产生了这个问题。

被截断的字段是表的主键,表的主键在DB设计长度是64,在应用程序中,主键= 批次号+订单号,批次号是外部系统返回字段,订单号由内部产生。测试同学在模块测试过程中,默认设置批次号为20190717_02,订单号长度固定为32位,主键一直都只有43位,远小于64位,该问题一直被隐藏;而后跟外部联调时,实际传入的批次号是34位,联调时已经出现主键被截断的错误,但是由于设置的sql_mode为空,数据截断插入不报error错误,流程正常运行结束,而且联调时又一笔通过导致问题在测试阶段被雪藏。

二:Server SQL Modes 介绍

2.1 sql_mode概念

sql_mode定义了MySQL应该支持的sql语法,对数据值和类型的校验程度。不同的sql_mode模式下导致mysql服务器在运行时结果不一样,有两种方式可以改变该值:
方法一:静态修改,修改配置文件(mysql的安装目录下my.ini)后重启mysql。
方法二:动态修改,sql_mode支持全局修改以及会话层修改。
全局查看和全局修改:SELECT @@GLOBAL.sql_mode; SET @@global.sql_mode= 'modes';全局修改就是影响整个数据库。需要超级特权(root)才能修改,global修改后,需要重新连接进来才会生效。
会话查看和会话修改:SELECT @@SESSION.sql_mode;SET SESSION sql_mode = 'modes';会话就是影响当前连接的会话,如果会话终止,设置的参数值失效。

2.2 sql_mode常用值说明

SQL Mode 定义了两个方面:MySQL应支持的SQL语法,以及应该在数据上执行何种确认检查。

2.2.1  常见的SQL语法支持类

    为了更好的举例说明不同的sql_mode对不同sql的影响,使用表t_payfund_log_20181121,其结构定义如下:

CREATE TABLE `t_payfund_log_20181121` (
`Fbank_seq` varchar() NOT NULL,
`Flast_bank_seq` varchar() DEFAULT NULL,
`Fstatus` smallint() NOT NULL DEFAULT '',
`Fuid` bigint() DEFAULT NULL,
`Flast_interface_id` int() DEFAULT NULL,
PRIMARY KEY (`Fbank_seq`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

表内数据暂且包含以下三条记录:

类型一:ONLY_FULL_GROUP_BY

   ONLY_FULL_GROUP_BY 对于GROUP BY聚合操作,如果在SELECT中的列、HAVING或者ORDER BY子句的列,没有在GROUP BY中出现,那么这个SQL是不合法的。是可以理解的,因为不在 group by 的列查出来展示会有矛盾。在5.7中默认启用,所以DB在从5.6升级到5.7的过程需要注意。

示例SQL语句:SELECT  Fstatus, Flast_interface_id FROM t_payfund_log_20181121 GROUP BY Fstatus;

A: sql_mode 为空:查询结果后正常返回

MySQL [loleina]> SELECT @@GLOBAL.sql_mode;
+-------------------+
| @@GLOBAL.sql_mode |
+-------------------+
| |
+-------------------+
row in set (0.00 sec) MySQL [loleina]> SELECT Fstatus, Flast_interface_id FROM t_payfund_log_20181121 GROUP BY Fstatus;
+---------+--------------------+
| Fstatus | Flast_interface_id |
+---------+--------------------+
| | |
| | |
+---------+--------------------+
rows in set (0.00 sec)
     B: sql_mode=‘ONLY_FULL_GROUP_BY ':查询直接报错   (设置后重新连接登录)
MySQL [loleina]> SET GLOBAL sql_mode ='ONLY_FULL_GROUP_BY';
Query OK, rows affected (0.00 sec) MySQL [loleina]> SELECT @@GLOBAL.sql_mode;
+--------------------+
| @@GLOBAL.sql_mode |
+--------------------+
| ONLY_FULL_GROUP_BY |
+--------------------+
row in set (0.00 sec) MySQL [loleina]> SELECT Fstatus, Flast_interface_id FROM t_payfund_log_20181121 GROUP BY Fstatus;
ERROR (): Expression # of SELECT list is not in GROUP BY clause and contains nonaggregated column 'loleina.t_payfund_log_20181121.Flast_interface_id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

当遇到这种情况,可以使用ANY_VALUE(),对于不符合ONLY_FULL_GROUP_BY的字段使用ANY_VALUE()函数,让MySQL跳过ONLY_FULL_GROUP_BY检测

MySQL [loleina]> SELECT  Fstatus, ANY_VALUE(Flast_interface_id) FROM t_payfund_log_20181121 GROUP BY Fstatus;
+---------+-------------------------------+
| Fstatus | ANY_VALUE(Flast_interface_id) |
+---------+-------------------------------+
| | |
| | |
+---------+-------------------------------+
rows in set (0.00 sec)

类型二:ANSI_QUOTES

启用 ANSI_QUOTES 后,不能用双引号来引用字符串,因为它被解释为识别符,作用与 ` 一样。

示例SQL语句: update t_payfund_log_20181121 set Flast_bank_seq="20" where Fbank_seq='23020190915090211222002';

A: sql_mode 为空:查询结果后正常返回.

MySQL [loleina]> SELECT @@GLOBAL.sql_mode;
+-------------------+
| @@GLOBAL.sql_mode |
+-------------------+
| |
+-------------------+
row in set (0.00 sec) MySQL [loleina]> update t_payfund_log_20181121 set Flast_bank_seq="" where Fbank_seq='';
Query OK, row affected (0.01 sec)
Rows matched: Changed: Warnings:

B: sql_mode=‘ANSI_QUOTES ':报 Unknown column '' in 'field list语法错误.

MySQL [loleina]> SELECT @@GLOBAL.sql_mode;
+-------------------+
| @@GLOBAL.sql_mode |
+-------------------+
| ANSI_QUOTES |
+-------------------+
row in set (0.00 sec) MySQL [loleina]> update t_payfund_log_20181121 set Flast_bank_seq="" where Fbank_seq='';
ERROR (42S22): Unknown column '' in 'field list'

类型三:PIPES_AS_CONCAT

在Oracle,||连接字符串的,而在mysql,是用CONCAT来实现的,mysql为了兼容这一个特性,设置了这个模式,mysql会将 将 || 视为字符串的连接操作符而非 或 运算符,

示例SQL语句:update t_payfund_log_20181121 set Fmemo=' '||Fmemo||'&pay failed' where Fbank_seq='23020190915090211222002';

等价于: update t_payfund_log_20181121 set Fmemo=CONCAT_WS(' ',Fmemo, '&pay failed') where Fbank_seq='23020190915090211222002';

A: sql_mode 为空:使用||报数据截断warnings

MySQL [loleina]> SELECT @@GLOBAL.sql_mode;
+-------------------+
| @@GLOBAL.sql_mode |
+-------------------+
| |
+-------------------+
row in set (0.00 sec) MySQL [loleina]> update t_payfund_log_20181121 set Fmemo=' '||Fmemo||'&pay failed' where Fbank_seq='';
Query OK, row affected, warnings (0.00 sec)
Rows matched: Changed: Warnings: MySQL [loleina]> show warnings;
+---------+------+-------------------------------------------------+
| Level | Code | Message |
+---------+------+-------------------------------------------------+
| Warning | | Truncated incorrect DOUBLE value: ' test2 ' |
| Warning | | Truncated incorrect DOUBLE value: '&pay failed' |
+---------+------+-------------------------------------------------+
rows in set (0.01 sec) MySQL [loleina]> SELECT * from loleina.t_payfund_log_20181121;
+-------------------------+----------------+---------+--------+--------------------+--------------------+
| Fbank_seq | Flast_bank_seq | Fstatus | Fuid | Flast_interface_id | Fmemo |
+-------------------------+----------------+---------+--------+--------------------+--------------------+
| | | | | | test1 &pay failed |
| | | | | | |
| | | | | | test3 |
+-------------------------+----------------+---------+--------+--------------------+--------------------+
rows in set (0.00 sec)

B: sql_mode=‘PIPES_AS_CONCAT  ':数据正常插入。

MySQL [loleina]> SELECT @@GLOBAL.sql_mode;
+-------------------+
| @@GLOBAL.sql_mode |
+-------------------+
| PIPES_AS_CONCAT |
+-------------------+
row in set (0.00 sec) MySQL [loleina]> SELECT * from loleina.t_payfund_log_20181121;
+-------------------------+----------------+---------+--------+--------------------+---------+
| Fbank_seq | Flast_bank_seq | Fstatus | Fuid | Flast_interface_id | Fmemo |
+-------------------------+----------------+---------+--------+--------------------+---------+
| | | | | | test1 |
| | | | | | test2 |
| | | | | | test3 |
+-------------------------+----------------+---------+--------+--------------------+---------+
rows in set (0.00 sec) MySQL [loleina]> update t_payfund_log_20181121 set Fmemo=' '||Fmemo||'&pay failed' where Fbank_seq='';
Query OK, row affected (0.01 sec)
Rows matched: Changed: Warnings: MySQL [loleina]> SELECT * from loleina.t_payfund_log_20181121;
+-------------------------+----------------+---------+--------+--------------------+--------------------+
| Fbank_seq | Flast_bank_seq | Fstatus | Fuid | Flast_interface_id | Fmemo |
+-------------------------+----------------+---------+--------+--------------------+--------------------+
| | | | | | test1 &pay failed |
| | | | | | test2 |
| | | | | | test3 |
+-------------------------+----------------+---------+--------+--------------------+--------------------+
rows in set (0.00 sec)

类型四:NO_AUTO_CREATE_USER

授权命令GRANT 语句的语法:GRANT privileges (columns) ON what TO user IDENTIFIED BY "password" WITH GRANT OPTION,其中IDENTIFIED BY是可选子句,用来于指定mysql用户的口令。既然是可选,那就是可以没有。如果没有指定IDENTIFIED BY,会怎么样?

答案是可能导致一个安全漏洞:如果user是指现有用户,那么没有任何影响;但如果user是指新用户,那么该用户将不被赋予口令,这样的语句会创建一个口令为空,且已有数据库操作权限的mysql用户!那怎么办呢?mysql自身已经给出了解决方案,将sql_mode设置为NO_AUTO_CREATE_USER,mysql会阻止任何创建空密码的用户。

NO_AUTO_CREATE_USER 字面意思不自动创建用户。在给MySQL用户授权时,我们习惯使用上述类似命令 GRANT ... ON ... TO dbuser 一起创建用户。设置该模式后就与oracle操作类似,授权之前须先建立用户,否则会报error错误。5.7.7开始也默认了。

示例SQL语句:GRANT ALL PRIVILEGES ON *.* TO 'root'@'10.43.24.149';

A: sql_mode 为空:存在安全漏洞

MySQL [loleina]> SELECT @@GLOBAL.sql_mode;
+-------------------+
| @@GLOBAL.sql_mode |
+-------------------+
| |
+-------------------+
row in set (0.00 sec) MySQL [loleina]> GRANT ALL PRIVILEGES ON *.* TO 'root'@'10.43.24.149';
Query OK, rows affected, warning (0.00 sec) MySQL [loleina]> GRANT ALL PRIVILEGES ON *.* TO 'root'@'10.43.24.19';
Query OK, rows affected, warning (0.00 sec)

B: sql_mode=‘NO_AUTO_CREATE_USER ':设置该模式:直接报错

MySQL [loleina]> SELECT @@GLOBAL.sql_mode;
+---------------------+
| @@GLOBAL.sql_mode |
+---------------------+
| NO_AUTO_CREATE_USER |
+---------------------+
row in set (0.00 sec)
MySQL [loleina]> GRANT ALL PRIVILEGES ON *.* TO 'root'@'10.43.24.149';
ERROR (): Can't find any matching row in the user table
MySQL [(none)]> GRANT ALL PRIVILEGES ON *.* TO 'root'@'10.43.24.22';
ERROR (): Can't find any matching row in the user table

那如果使用IDENTIFIED BY子句呢?虽然不会直接报error错误,但是waring错误已经明确给出”不赞成使用grant创建新用户,会在以后的版本中删除。使用CREATEUSER语句创建新用户。”

MySQL [(none)]> GRANT ALL PRIVILEGES ON *.* TO 'root'@'10.43.24.22' IDENTIFIED BY 'root1234' WITH GRANT OPTION;
Query OK, rows affected, warning (0.00 sec) MySQL [(none)]> show warnings;
+---------+------+------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+---------+------+------------------------------------------------------------------------------------------------------------------------------------+
| Warning | | Using GRANT for creating new user is deprecated and will be removed in future release. Create new user with CREATE USER statement. |
+---------+------+------------------------------------------------------------------------------------------------------------------------------------+
row in set (0.00 sec)

2.2.2 数据检查类

类型五:NO_ZERO_DATE 

NO_ZERO_DATE 认为日期 '0000-00-00' 是否非法,与是否设置后面的严格模式有关。 在严格模式,只有‘0000-00-00’报错,输入‘1000-00-00’不报错; 在非严格模式,可以接受该‘0000-00-00’但会生成警告。

示例sql语句:

INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Fuid`, `Flast_interface_id`,  `Fpay_time`, `Fend_time`) VALUES ('23020190915090211222009',  '540977', '998501', 0000-00-00, 0000-00-00);

A: sql_mode 为空:'0000-00-00' 能合法插入

MySQL [loleina]> SELECT @@GLOBAL.sql_mode;
+-------------------+
| @@GLOBAL.sql_mode |
+-------------------+
| |
+-------------------+
row in set (0.00 sec) MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Fuid`, `Flast_interface_id`, `Fpay_time`, `Fend_time`) VALUES ('', '', '', --, --);
Query OK, row affected (0.00 sec) MySQL [loleina]> select * from t_payfund_log_20181121 \g
+-------------------------+----------------+---------+--------+--------------------+--------------------+----------------------------+---------------------+
| Fbank_seq | Flast_bank_seq | Fstatus | Fuid | Flast_interface_id | Fmemo | Fpay_time | Fend_time |
+-------------------------+----------------+---------+--------+--------------------+--------------------+----------------------------+---------------------+
| | | | | | test1 &pay failed | NULL | NULL |
| | | | | | | NULL | NULL |
| | | | | | test3 | NULL | NULL |
| | NULL | | | | NULL | -- ::00.000000 | -- :: |
+-------------------------+----------------+---------+--------+--------------------+--------------------+----------------------------+---------------------+
rows in set (0.00 sec)

B: 非严格模式设置sql_mode =‘NO_ZERO_DATE’,输入日期=‘0000-00-00'’ 能插入但会产生告警信息:

MySQL [loleina]> SELECT @@GLOBAL.sql_mode;
+-------------------+
| @@GLOBAL.sql_mode |
+-------------------+
| NO_ZERO_DATE |
+-------------------+
row in set (0.00 sec) MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Fuid`, `Flast_interface_id`, `Fpay_time`, `Fend_time`) VALUES ('', '', '', --, --);
Query OK, row affected, warnings (0.00 sec) MySQL [loleina]> show warnings;
+---------+------+----------------------------------------------------+
| Level | Code | Message |
+---------+------+----------------------------------------------------+
| Warning | | Out of range value for column 'Fpay_time' at row |
| Warning | | Out of range value for column 'Fend_time' at row |
+---------+------+----------------------------------------------------+
rows in set (0.00 sec) MySQL [loleina]> select * from t_payfund_log_20181121 \g
+-------------------------+----------------+---------+--------+--------------------+--------------------+----------------------------+---------------------+
| Fbank_seq | Flast_bank_seq | Fstatus | Fuid | Flast_interface_id | Fmemo | Fpay_time | Fend_time |
+-------------------------+----------------+---------+--------+--------------------+--------------------+----------------------------+---------------------+
| | | | | | test1 &pay failed | NULL | NULL |
| | | | | | | NULL | NULL |
| | | | | | test3 | NULL | NULL |
| | NULL | | | | NULL | -- ::00.000000 | -- :: |
| | NULL | | | | NULL | -- ::00.000000 | -- :: |
+-------------------------+----------------+---------+--------+--------------------+--------------------+----------------------------+---------------------+
rows in set (0.00 sec)

C: 严格模式下设置sql_mode =‘NO_ZERO_DATE’,输入日期=‘0000-00-00'’ 插入直接报错,输入‘000-00-00’则不报错

MySQL [loleina]>  SELECT @@GLOBAL.sql_mode;
+----------------------------------+
| @@GLOBAL.sql_mode |
+----------------------------------+
| STRICT_TRANS_TABLES,NO_ZERO_DATE |
+----------------------------------+
row in set (0.00 sec) MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Fuid`, `Flast_interface_id`, `Fpay_time`, `Fend_time`) VALUES ('', '', '', --, --);
ERROR (): Incorrect datetime value: '' for column 'Fpay_time' at row 1 INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Fuid`, `Flast_interface_id`,  `Fpay_time`, `Fend_time`) VALUES ('23020190915090211222009',  '540977', '998501', 1000-00-00, '1999-01-01 11:11:11');
Query OK, 1 row affected (0.00 sec)

类型六:NO_ZERO_IN_DATE

NO_ZERO_IN_DATE 认为日期 '0000-00-00' 是否非法,与是否设置后面的严格模式有关。 在严格模式,只有‘0000-00-00’能正常插入,输入‘1000-00-00’报错; 在非严格模式,可以接受‘0000-00-00’,但会生成警告。NO_ZERO_IN_DATE :MySQL中插入的时间字段值,支持‘0000-00-00’插入, 但是只要日期的月和日中含有0值就跟严格模式设置有关。

    示例sql语句:

 INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Fuid`, `Flast_interface_id`,  `Fpay_time`, `Fend_time`) VALUES ('23020190915090211222010',  '540977', '998501', 0000-00-00, 0000-00-00);

A: 非严格模式设置sql_mode =‘NO_ZERO_IN_DATE  ’,支持日期=‘0000-00-00',输入日期=‘1000-00-00'则告警

MySQL [loleina]> SELECT @@GLOBAL.sql_mode; 
+-------------------+
| @@GLOBAL.sql_mode |
+-------------------+
| NO_ZERO_IN_DATE   |
+-------------------+
1 row in set (0.00 sec) MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Fuid`, `Flast_interface_id`,  `Fpay_time`, `Fend_time`) VALUES ('23020190915090211222010',  '540977', '998501', 0000-00-00, 0000-00-00);
Query OK, 1 row affected (0.00 sec) MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Fuid`, `Flast_interface_id`,  `Fpay_time`, `Fend_time`) VALUES ('23020190915090211222011',  '540977', '998501', 1000-00-00, 0000-00-00);
Query OK, 1 row affected, 1 warning (0.00 sec) MySQL [loleina]> show warnings;
+---------+------+------------------------------------------------+
| Level   | Code | Message                                        |
+---------+------+------------------------------------------------+
| Warning | 1265 | Data truncated for column 'Fpay_time' at row 1 |
+---------+------+------------------------------------------------+
1 row in set (0.00 sec) MySQL [loleina]> SELECT * FROM t_payfund_log_20181121 WHERE Fbank_seq = '23020190915090211222012' \g
+-------------------------+----------------+---------+--------+--------------------+-------+----------------------------+---------------------+
| Fbank_seq               | Flast_bank_seq | Fstatus | Fuid   | Flast_interface_id | Fmemo | Fpay_time                  | Fend_time           |
+-------------------------+----------------+---------+--------+--------------------+-------+----------------------------+---------------------+
| 23020190915090211222012 | NULL           |       0 | 540977 |             998501 | NULL  | 0000-00-00 00:00:00.000000 | 0000-00-00 00:00:00 |
+-------------------------+----------------+---------+--------+--------------------+-------+----------------------------+---------------------+
1 row in set (0.00 sec)

B: 严格模式下设置sql_mode =‘NO_ZERO_IN_DATE   ’,输入日期=‘0000-00-00'’告警,输入‘1000-00-00’则直接报错

MySQL [loleina]> SELECT @@GLOBAL.sql_mode;
+-------------------------------------+
| @@GLOBAL.sql_mode |
+-------------------------------------+
| STRICT_TRANS_TABLES,NO_ZERO_IN_DATE |
+-------------------------------------+
row in set (0.00 sec) MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Fuid`, `Flast_interface_id`, `Fpay_time`, `Fend_time`) VALUES ('', '', '', --, --);
Query OK, row affected (0.00 sec) MySQL [loleina]> SELECT * FROM t_payfund_log_20181121 WHERE Fbank_seq = '' \g
+-------------------------+----------------+---------+--------+--------------------+-------+----------------------------+---------------------+
| Fbank_seq | Flast_bank_seq | Fstatus | Fuid | Flast_interface_id | Fmemo | Fpay_time | Fend_time |
+-------------------------+----------------+---------+--------+--------------------+-------+----------------------------+---------------------+
| | NULL | | | | NULL | -- ::00.000000 | -- :: |
+-------------------------+----------------+---------+--------+--------------------+-------+----------------------------+---------------------+
row in set (0.00 sec) MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Fuid`, `Flast_interface_id`, `Fpay_time`, `Fend_time`) VALUES ('', '', '', --, --);
ERROR (): Incorrect datetime value: '' for column 'Fpay_time' at row

类型七:STRICT_TRANS_TABLES

 严格模式,STRICT_TRANS_TABLES 不是几种策略的组合,单独指 INSERTUPDATE出现少值或无效值该如何处理,考虑以下5种情况:

1、整型字段数据超长,DB设置Fstatus长度为6,实际插入数据为12345678

2、字符串字段数据超长,DB设置Fmemo长度为25,实际插入数据为30.FmemoFmemoFmemoFmemoFmemoFmemo

3、设置Fstatu的默认值为0,插入时带这个字段,数据为''

4、设置Fstatu的默认值为空,插入时不带这个字段

5、Fststus字段为int类型,但是插入string类型的数据:abc

A: sql_mode 为空:报error错误:

1、整型字段长度超长溢出,mysql自动校正为类型最长值插入DB,风险很高

MySQL [loleina]> SELECT @@GLOBAL.sql_mode;
+-------------------+
| @@GLOBAL.sql_mode |
+-------------------+
| |
+-------------------+
row in set (0.01 sec) MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Flast_bank_seq`, `Fstatus`, `Fuid`, `Flast_interface_id`, `Fmemo`, `Fpay_time`) VALUES ('', '', '', '', '', 'Fmemo', NULL);
Query OK, row affected, warning (0.00 sec) MySQL [loleina]> show warnings;
+---------+------+--------------------------------------------------+
| Level | Code | Message |
+---------+------+--------------------------------------------------+
| Warning | | Out of range value for column 'Fstatus' at row |
+---------+------+--------------------------------------------------+
row in set (0.00 sec) MySQL [loleina]> select * from t_payfund_log_20181121 where Fbank_seq='' \G
*************************** . row ***************************
Fbank_seq:
Flast_bank_seq:
Fstatus:
Fuid:
Flast_interface_id:
Fmemo: Fmemo
Fpay_time: NULL
row in set (0.00 sec)

2、字符串字段数据超长被截断保存到DB

MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Flast_bank_seq`, `Fstatus`, `Fuid`, `Flast_interface_id`, `Fmemo`, `Fpay_time`) VALUES ('', '', '', '', '', 'FmemoFmemoFmemoFmemoFmemoFmemo', NULL);
Query OK, row affected, warning (0.00 sec) MySQL [loleina]> show warnings;
+---------+------+--------------------------------------------+
| Level | Code | Message |
+---------+------+--------------------------------------------+
| Warning | | Data truncated for column 'Fmemo' at row |
+---------+------+--------------------------------------------+
row in set (0.00 sec) MySQL [loleina]> select * from t_payfund_log_20181121 where Fbank_seq='' \G
*************************** . row ***************************
Fbank_seq:
Flast_bank_seq:
Fstatus:
Fuid:
Flast_interface_id:
Fmemo: FmemoFmemoFmemoFmemo
Fpay_time: NULL
row in set (0.00 sec)

3、设置Fstatu的默认值为0,插入时带这个字段,但数据为'',实际插入为0

MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Flast_bank_seq`, `Fstatus`, `Fuid`, `Flast_interface_id`, `Fmemo`, `Fpay_time`) VALUES ('', '', '', '', '', 'FmemoFmemoFmemoFmemoFmemoFmemo', NULL);
Query OK, row affected, warning (0.00 sec) MySQL [loleina]> show warnings;
+---------+------+--------------------------------------------+
| Level | Code | Message |
+---------+------+--------------------------------------------+
| Warning | | Data truncated for column 'Fmemo' at row |
+---------+------+--------------------------------------------+
row in set (0.00 sec) MySQL [loleina]> select * from t_payfund_log_20181121 where Fbank_seq='' \G
*************************** . row ***************************
Fbank_seq:
Flast_bank_seq:
Fstatus:
Fuid:
Flast_interface_id:
Fmemo: FmemoFmemoFmemoFmemo
Fpay_time: NULL
row in set (0.00 sec)

4、设置Fstatu的默认值为空,插入时不带这个字段,实际插入0

MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Flast_bank_seq`, `Fuid`, `Flast_interface_id`, `Fmemo`) VALUES ('23020190917090211222004', '3', '44322', '997601', 'Fmemo');
Query OK, 1 row affected, 1 warning (0.01 sec) MySQL [loleina]> show warnings;
+---------+------+----------------------------------------------+
| Level   | Code | Message                                      |
+---------+------+----------------------------------------------+
| Warning | 1364 | Field 'Fstatus' doesn't have a default value |
+---------+------+----------------------------------------------+
1 row in set (0.00 sec) MySQL [loleina]> select *  from t_payfund_log_20181121 where Fbank_seq='23020190917090211222004' \G
*************************** 1. row ***************************
         Fbank_seq: 23020190917090211222004
    Flast_bank_seq: 3
           Fstatus: 0
              Fuid: 44322
Flast_interface_id: 997601
             Fmemo: Fmemo
         Fpay_time: NULL
1 row in set (0.00 sec)

5、Fststus字段为int类型,但是插入string类型的数据:abc,实际插入为0

MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Flast_bank_seq`, `Fstatus`, `Fuid`, `Flast_interface_id`, `Fmemo`, `Fpay_time`) VALUES ('', '', 'ABC', '', '', 'Fmemo', NULL);
Query OK, row affected, warning (0.00 sec) MySQL [loleina]> show warnings;
+---------+------+--------------------------------------------------------------+
| Level | Code | Message |
+---------+------+--------------------------------------------------------------+
| Warning | | Incorrect integer value: 'ABC' for column 'Fstatus' at row |
+---------+------+--------------------------------------------------------------+
row in set (0.00 sec) MySQL [loleina]> select * from t_payfund_log_20181121 where Fbank_seq='' \G
Empty set (0.00 sec) MySQL [loleina]> select * from t_payfund_log_20181121 where Fbank_seq='' \G
*************************** . row ***************************
Fbank_seq:
Flast_bank_seq:
Fstatus:
Fuid:
Flast_interface_id:
Fmemo: Fmemo
Fpay_time: NULL
row in set (0.00 sec)

B : sql_mode =‘STRICT_TRANS_TABLES ’设置为严格模式,1-5插入数据全失败,报error错误:

MySQL [loleina]> SELECT @@GLOBAL.sql_mode;
+---------------------+
| @@GLOBAL.sql_mode |
+---------------------+
| STRICT_TRANS_TABLES |
+---------------------+
row in set (0.00 sec) 整型超长
MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Flast_bank_seq`, `Fstatus`, `Fuid`, `Flast_interface_id`, `Fmemo`, `Fpay_time`) VALUES ('', '', '', '', '', 'Fmemo', NULL);
ERROR (): Out of range value for column 'Fstatus' at row 字符串超长
MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Flast_bank_seq`, `Fstatus`, `Fuid`, `Flast_interface_id`, `Fmemo`, `Fpay_time`) VALUES ('', '', '', '', '', 'FmemoFmemoFmemoFmemoFmemoFmemo', NULL);
ERROR (): Data too long for column 'Fmemo' at row 设置Fstatu的默认值为0,插入时带这个字段,但是设置为''
MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Flast_bank_seq`, `Fstatus`, `Fuid`, `Flast_interface_id`, `Fmemo`, `Fpay_time`) VALUES ('', '', '', '', '', Fmemo, NULL);
ERROR (HY000): Incorrect integer value: '' for column 'Fstatus' at row 设置Fstatu的默认值为''
MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Flast_bank_seq`, `Fuid`, `Flast_interface_id`, `Fmemo`) VALUES ('', '', '', '', 'Fmemo');
ERROR (HY000): Field 'Fstatus' doesn't have a default value Fststus为int,但是插入string类型
MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Flast_bank_seq`, `Fstatus`, `Fuid`, `Flast_interface_id`, `Fmemo`, `Fpay_time`) VALUES ('', '', 'ABC', '', '', 'Fmemo', NULL);
ERROR (HY000): Incorrect integer value: 'ABC' for column 'Fstatus'

2.3、三个重要且常用的sql_mode模式

2.3.1ANSI

此模式下,更接近标准sql。包含 REAL_AS_FLOATPIPES_AS_CONCATANSI_QUOTESIGNORE_SPACE, ONLY_FULL_GROUP_BY.

宽松模式,对插入数据进行校验,如果不符合定义类型或长度,会对数据类型调整或截断保存,报warning警告。

2.3.2 STRICT_TRANS_TABLES

该选项针对事务性存储引擎生效,对于非事务性存储引擎无效,该选项表示开启strict sql模式。在strict sql模式下,在INSERT或者UPDATE语句中,插入或者更新了某个不符合规定的字段值,则会直接报错中断操作。包含: ERROR_FOR_DIVISION_BY_ZERONO_ZERO_DATE, and NO_ZERO_IN_DATE modes. 从mysql 5.7.5开始,默认的sql模式包括此模式。

严格模式,进行数据的严格校验,错误数据不能插入,报error错误。

2.3.3 TRADITIONAL

严格模式,当向mysql数据库插入数据时,进行数据的严格校验,保证错误数据不能插入,报error错误。用于事务时,会进行事物的回滚。

三:测试策略

3.1 小结:sql_mode=''和其他模式下影响的sql以及结果对比

3.2 宽松模式下测试应该注意哪些地方?

1、怎么查看出现的warings?

  使用show warnings查看最新warings日志,但在多客户端使用的情况下,这个日志很快就会被刷新而捕捉不到。  在非严格模式下,一些mysql语句会报error错,这类error错误不会记日志,使用show warnings可以看见。show warnings是一个诊断语句,它显示有关在当前会话中最新的执行语句所导致的条件(错误、警告和注释)的信息2、可以把现网的sql_mode更改为严格模式来防止一些问题的产生吗?
    实际一般现网运营的DB设置的模式都是严格模式,连mysql的5.7以上的版本默认都是严格模式,但是可能有些系统运行很久了,在mysql进行升级的时候,可以把sql_mode从原来的宽松模式更改为严格模式吗?
现网是不可以的,mysql官网明确指出:“在创建好表和插入数据到分区表中之后,更改服务器SQL模式可能会导致此类表的行为发生重大变化,并可能导致数据丢失或损坏。强烈建议在使用用户定义的分区创建表后,不
要更改sql模式。当复制分区表时,主和从机上不同的SQL模式也会导致问题。为了获得最佳结果,您应该始终在主服务器和从服务器上使用相同的服务器sql模式。” 3、宽松模式下使用mysql数据库测试需要注意什么呢? A:注意表的字段的长度和数据类型设计是否合理。因为在长度设计过短或者数据类型设计错误时,宽松模式下插入或者更新是不会直接报error错误的。
B: 检查每一笔数据插入时,字段值的正确性。
 
  

缺陷的背后(三)---mysql之sql_mode为空的陷阱的更多相关文章

  1. Mysql基础(三):MySQL基础数据类型、完整性约束、sql_mode模式

    目录 2.MySQL基础数据类型.完整性约束.sql_mode模式 1. MySQL常用数据类型 2. 完整性约束 3. MySQL的sql_mode模式说明以及设置 2.MySQL基础数据类型.完整 ...

  2. Mysql的sql_mode

    (一) 基本介绍 set sql_mode="",即强制不设定MySql模式(如不作输入检测.错误提示.语法模式检查等)应该能提高性能,但有如下问题: 如果插入了不合适数据(错误类 ...

  3. MySQL的sql_mode模式说明及设置

    MySQL的sql_mode模式说明及设置 MySQL的sql_mode合理设置 sql_mode是个很容易被忽视的变量,默认值是空值,在这种设置下是可以允许一些非法操作的,比如允许一些非法数据的插入 ...

  4. MySQL的sql_mode解析与设置,sql文件导入报错解决

    在往MySQL数据库中插入一组数据时,出错了!数据库无情了给我报了个错误:ERROR 1365(22012):Division by 0:意思是说:你不可以往数据库中插入一个 除数为0的运算的结果.于 ...

  5. MySQL的sql_mode解析与设置

    https://blog.csdn.net/hhq163/article/details/54140286 https://blog.csdn.net/ccccalculator/article/de ...

  6. 使用MySQL,SQL_MODE有哪些坑,你知道么?

    SQL_MODE是MySQL中的一个系统变量(variable),可由多个MODE组成,每个MODE控制一种行为,如是否允许除数为0,日期中是否允许'0000-00-00'值. 为什么需要关注SQL_ ...

  7. MySQL的sql_mode模式 解决数据Incorrect DECIMAL value: ‘0’ for column ” at row -1问题

    https://blog.csdn.net/weiwoyonzhe/article/details/85177294?depth_1-utm_source=distribute.pc_relevant ...

  8. mysql的sql_mode模式

    在oracle或sqlserver中,如果某个表的字段设置成not null,insert或update时不给这个字段赋值,比如下面这样: 表t_test(id,name)中id,name都不允许为空 ...

  9. mysql的sql_mode合理设置

    mysql的sql_mode合理设置 sql_mode是个很容易被忽视的变量,默认值是空值,在这种设置下是可以允许一些非法操作的,比如允许一些非法数据的插入.在生产环境必须将这个值设置为严格模式,所以 ...

随机推荐

  1. nRF51822 的两路 PWM 极性

    忙了一阵这个PWM,玩着玩着终于发现了些规律.Nordic 也挺会坑爹的. nRF51822 是没有硬件 PWM 的,只能靠一系列难以理解的 PPI /GPIOTE/TIMER来实现,其实我想说,我醉 ...

  2. GO语言GIN框架入门

    Gin框架介绍 Gin是一个用Go语言编写的web框架.它是一个类似于martini但拥有更好性能的API框架, 由于使用了httprouter,速度提高了近40倍. 中文文档 Gin框架安装与使用 ...

  3. [C++] namespace命名空间和using用法

    命名空间namespace:指标识符的各种可见范围. C++标准程序库中的所有标识符都被定义在一个std的namespace,这就是程序开始添加 using namespace std; 的原因. 很 ...

  4. 第06组 Beta冲刺(1/5)

    队名:拾光组 组长博客链接 作业博客链接 团队项目情况 燃尽图(组内共享) 组长:宋奕 过去两天完成了哪些任务 准备beta冲刺的内容和分工 修改了后端的一些bug GitHub签入记录 接下来的计划 ...

  5. python 玩爬虫安装了一大堆第三方库

    之前就听说过爬虫,感觉很复杂的样子,但是看到python代码很简短.由于本机已经安装了python2.7 所以就拿来py 文件跑一下想看看效果. 结果各种代码错误.然后根据每个错误去下载对应的依赖项. ...

  6. Python多进程和多线程是鸡肋嘛?【转】

    GIL是什么 Python的代码执行由 Python虚拟机(也叫解释器主循环,CPython版本)来控制,Python在设计之初就考虑到在解释器的主循环中,同时只有一个线程在运行.即每个CPU在任意时 ...

  7. 堆排序Heapsort的Java和C代码

    Heapsort排序思路 将整个数组看作一个二叉树heap, 下标0为堆顶层, 下标1, 2为次顶层, 然后每层就是"3,4,5,6", "7, 8, 9, 10, 11 ...

  8. typescript - 9.装饰器

    装饰器:装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为. 通俗的讲装饰器就是一个方法,可以注入到类.方法.属性参数上来扩展类.属性.方法.参数的功能. 常见的装 ...

  9. openresty开发系列37--nginx-lua-redis实现访问频率控制

    openresty开发系列37--nginx-lua-redis实现访问频率控制 一)需求背景 在高并发场景下为了防止某个访问ip访问的频率过高,有时候会需要控制用户的访问频次在openresty中, ...

  10. Spark资源调度及任务调度

    1.  资源分配 通过SparkSubmit进行提交应用后,首先会创建Client将应用程序(字节码文件.class)包装成Driver,并将其注册到Master.Master收到Client的注册请 ...