事故回溯

某业务流程操作为:

1、循环扫描某张待处理请求表,查看是否有请求等待处理。
2、找到待处理请求后,申请相关资源进行处理,并将处理结果插入到处理结果表中。
3、将该请求从待处理请求表中移除。

由于业务需求变更,研发同事申请向处理结果表上新增一个NOT NULL的列,但没有为该列设置默认值,数据库运行在严格模式下,DDL执行成功后,业务系统未及时上线,仍使用原有代码继续执行,在执行到步骤2向处理结果表插入记录时抛出异常,导致流程操作失败并循环重试,知道所有资源被耗光,导致严重的业务问题。

在该事故中,一个主要的问题就是DBA使用MySQL Inception对SQL进行语法检查,未发现NOT NULL的列没有默认值,同时对该问题存在理解偏差。

在SQL Server中,不允许在有记录的数据表上增加"NOT NULL且没有默认值"的列,但在MySQL上是否存在例外呢?

SQL Mode 与 NOT NULL

MySQL允许设置不同的SQL MODE(GLOBAL级别或SESSION级别)来兼容MySQL早期版本或兼容非标准SQL,比较常用的SQL MODE两种是严格模式(strict mode)和非严格模式(non-strict mode),两者差异较大,需要慎重设置。

在MySQL 官方文档中有如下关于SQL MODE和NOT NULL的描述:

Strict mode controls how MySQL handles invalid or missing values in data-change statements such as INSERT or UPDATE. A value can be invalid for several reasons. For example, it might have the wrong data type for the column, or it might be out of range. A value is missing when a new row to be inserted does not contain a value for a non-NULL column that has no explicit DEFAULT clause in its definition. (For a NULL column, NULL is inserted if the value is missing.) Strict mode also affects DDL statements such as CREATE TABLE.

Strict SQL mode applies to the following errors, represent a class of errors in which an input value is either invalid or missing. A value is invalid if it has the wrong data type for the column or might be out of range. A value is missing if a new row to be inserted does not contain a value for a NOT NULL column that has no explicit DEFAULT clause in its definition.

Changing a NULL column to NOT NULL in non-strict mode is prohibited to prevent converting NULL values to default non-NULL values, for which there are no corresponding values in the referenced table. The operation is permitted in strict mode, but an error is returned if any such conversion is required.

https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sql-mode-strict

准备测试环境

# 删除已有测试表
drop table if exists tb1001; # 创建测试表
create table tb1001(
id int auto_increment primary key,
c1 int
); # 插入测试数据
insert into tb1001(c1) select ;
insert into tb1001(c1) select ;
insert into tb1001(c1) select ; # 查看最新数据
select * from tb1001;
+----+------+
| id | c1 |
+----+------+
| | |
| | |
| | |
+----+------+

严格模式下NOT NULL限制

设置SESSION级别严格模式

set session sql_mode='STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'

测试1、增加不带默认值的NOT NULL列,测试通过,对已有记录,会付给该类型的初始值(PS:时间戳列比较特殊,会自动生成默认值,且不同版本存在差异)

alter table tb1001
add c2 int not null,
add c3 varchar(10) not null,
add c4 datetime not null,
add c5 timestamp not null;
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0 select * from tb1001;
+----+------+----+----+---------------------+---------------------+
| id | c1 | c2 | c3 | c4 | c5 |
+----+------+----+----+---------------------+---------------------+
| 1 | 1 | 0 | | 0000-00-00 00:00:00 | 2019-08-07 22:31:53 |
| 2 | 2 | 0 | | 0000-00-00 00:00:00 | 2019-08-07 22:31:53 |
| 3 | 3 | 0 | | 0000-00-00 00:00:00 | 2019-08-07 22:31:53 |
+----+------+----+----+---------------------+---------------------+
3 rows in set (0.00 sec)

测试2、插入记录但不显式设置"NOT NULL且没有默认值"列的值,测试失败,在严格模式下,必须为"NOT NULL且没有默认值"的列在插入时显式指定值

insert into tb1001(c1) select 4;
ERROR 1364 (HY000): Field 'c2' doesn't have a default value insert into tb1001(c1,c2) select 4,4;
ERROR 1364 (HY000): Field 'c3' doesn't have a default value insert into tb1001(c1,c2,c3) select 4,4,4;
ERROR 1364 (HY000): Field 'c4' doesn't have a default value insert into tb1001(c1,c2,c3,c4) select 4,4,4,now();
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0

测试3、插入记录时显式将"NOT NULL且没有默认值"的列设置为NULL,测试失败,在严格模式下,插入记录时不能将"NOT NULL且没有默认值"的列显式赋值为NULL

insert into tb1001(c1,c2,c3,c4) select 5,NULL,NULL,NULL;
ERROR 1048 (23000): Column 'c2' cannot be null insert into tb1001(c1,c2,c3,c4) select 5,5,NULL,NULL;
ERROR 1048 (23000): Column 'c3' cannot be null insert into tb1001(c1,c2,c3,c4) select 5,5,'',NULL;
ERROR 1048 (23000): Column 'c4' cannot be null insert into tb1001(c1,c2,c3,c4) select 5,5,'',NOW();
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0

测试4、更新记录时显式将"NOT NULL且没有默认值"的列设置为NULL,测试失败,在严格模式下,更新记录时不能将"NOT NULL且没有默认值"的列显式赋值为NULL。

UPDATE tb1001 SET C1=11, C2=NULL, C3=NULL, C4=NULL WHERE ID=1;
ERROR 1048 (23000): Column 'c2' cannot be null UPDATE tb1001 SET C1=11, C2=11, C3=NULL, C4=NULL WHERE ID=1;
ERROR 1048 (23000): Column 'c3' cannot be null UPDATE tb1001 SET C1=11, C2=11, C3='', C4=NULL WHERE ID=1;
ERROR 1048 (23000): Column 'c4' cannot be null UPDATE tb1001 SET C1=11, C2=11, C3='', C4=NOW() WHERE ID=1;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0

非严格模式下NOT NULL限制

设置SESSION级别非严格模式

set session sql_mode='ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'

测试1、增加不带默认值的NOT NULL列,测试通过,对已有记录,会付给该类型的初始值(PS:时间戳列比较特殊,会自动生成默认值,且不同版本存在差异)

alter table tb1001
add c2 int not null,
add c3 varchar(10) not null,
add c4 datetime not null,
add c5 timestamp not null;
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0 select * from tb1001;
+----+------+----+----+---------------------+---------------------+
| id | c1 | c2 | c3 | c4 | c5 |
+----+------+----+----+---------------------+---------------------+
| 1 | 1 | 0 | | 0000-00-00 00:00:00 | 2019-08-07 22:47:37 |
| 2 | 2 | 0 | | 0000-00-00 00:00:00 | 2019-08-07 22:47:37 |
| 3 | 3 | 0 | | 0000-00-00 00:00:00 | 2019-08-07 22:47:37 |
+----+------+----+----+---------------------+---------------------+
3 rows in set (0.00 sec)

测试2、插入记录但不显式设置"NOT NULL且没有默认值"列的值,测试通过,在非严格模式下,如果对"NOT NULL且没有默认值"的列在插入时未指定显式值则使用类型初始值。

insert into tb1001(c1) select 4;
Query OK, 1 row affected, 3 warnings (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 3 insert into tb1001(c1,c2) select 4,4;
Query OK, 1 row affected, 2 warnings (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 2 insert into tb1001(c1,c2,c3) select 4,4,4;
Query OK, 1 row affected, 1 warning (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 1 insert into tb1001(c1,c2,c3,c4) select 4,4,4,now();
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0 select * from tb1001 where c1=4;
+----+------+----+----+---------------------+---------------------+
| id | c1 | c2 | c3 | c4 | c5 |
+----+------+----+----+---------------------+---------------------+
| 4 | 4 | 0 | | 0000-00-00 00:00:00 | 2019-08-07 22:50:01 |
| 5 | 4 | 4 | | 0000-00-00 00:00:00 | 2019-08-07 22:50:01 |
| 6 | 4 | 4 | 4 | 0000-00-00 00:00:00 | 2019-08-07 22:50:01 |
| 7 | 4 | 4 | 4 | 2019-08-07 22:50:01 | 2019-08-07 22:50:01 |
+----+------+----+----+---------------------+---------------------+
4 rows in set (0.00 sec)

测试3、插入记录时显式将"NOT NULL且没有默认值"的列设置为NULL,测试通过,在非严格模式下,插入记录时可以将"NOT NULL且没有默认值"的列显式赋值为NULL,实际会存储列类型的初始值。

insert into tb1001(c1,c2,c3,c4) select 5,NULL,NULL,NULL;
Query OK, 1 row affected, 3 warnings (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 3 insert into tb1001(c1,c2,c3,c4) select 5,5,NULL,NULL;
Query OK, 1 row affected, 2 warnings (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 2 insert into tb1001(c1,c2,c3,c4) select 5,5,'',NULL;
Query OK, 1 row affected, 1 warning (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 1 insert into tb1001(c1,c2,c3,c4) select 5,5,'',NOW();
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0 select * from tb1001 where c1=5;
+----+------+----+----+---------------------+---------------------+
| id | c1 | c2 | c3 | c4 | c5 |
+----+------+----+----+---------------------+---------------------+
| 8 | 5 | 0 | | 0000-00-00 00:00:00 | 2019-08-07 22:54:05 |
| 9 | 5 | 5 | | 0000-00-00 00:00:00 | 2019-08-07 22:54:05 |
| 10 | 5 | 5 | 5 | 0000-00-00 00:00:00 | 2019-08-07 22:54:05 |
| 11 | 5 | 5 | 5 | 2019-08-07 22:54:05 | 2019-08-07 22:54:05 |
+----+------+----+----+---------------------+---------------------+
4 rows in set (0.00 sec)

测试4、更新记录时显式将"NOT NULL且没有默认值"的列设置为NULL,测试失败,在非严格模式下,更新记录时可以将"NOT NULL且没有默认值"的列显式赋值为NULL,实际会存储列类型的初始值。

UPDATE tb1001 SET C1=11, C2=11, C3='', C4='1911-11-11 11:11:11' WHERE ID=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0 ## 更新前数据
select * from tb1001 where id=1;
+----+------+----+----+---------------------+---------------------+
| id | c1 | c2 | c3 | c4 | c5 |
+----+------+----+----+---------------------+---------------------+
| 1 | 11 | 11 | 11 | 1911-11-11 11:11:11 | 2019-08-07 22:58:49 |
+----+------+----+----+---------------------+---------------------+
1 row in set (0.00 sec) UPDATE tb1001 SET C1=1, C2=NULL, C3=NULL, C4=NULL WHERE ID=1;
Query OK, 1 row affected, 3 warnings (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 3 ## 更新后数据
select * from tb1001 where id=1;
+----+------+----+----+---------------------+---------------------+
| id | c1 | c2 | c3 | c4 | c5 |
+----+------+----+----+---------------------+---------------------+
| 1 | 1 | 0 | | 0000-00-00 00:00:00 | 2019-08-07 22:59:34 |
+----+------+----+----+---------------------+---------------------+
1 row in set (0.00 sec)

总结

1、无论在严格模式还是非严格模式,都允许在已有记录的表上新增"NOT NULL且没有默认值"的列。

2、在严格模式下,不允许在插入时对"NOT NULL且没有默认值"的列未指定值或指定NULL值,不允许更新时对"NOT NULL且没有默认值"的列指定NULL值。

3、在非严格模式下,在插入时对"NOT NULL且没有默认值"的列未指定值或指定NULL值,或更新时对"NOT NULL且没有默认值"的列指定NULL值,都将使用列类型的初始值来替换NULL值。

MySQL Case--Strict mode与NOT NULL的更多相关文章

  1. mysql case when 判断null

    select  name,case WHEN m.NAME is null THEN '' else m.NAME end NAME1 from  sys_users

  2. MySQL中order by中关于NULL值的排序问题

    MySQL中order by 排序遇到NULL值的问题 MySQL数据库,在order by排序的时候,如果存在NULL值,那么NULL是最小的,ASC正序排序的话,NULL值是在最前面的. 如果我们 ...

  3. case when then 中判断null的方法

    --下列SQL无效 SELECT CASE MAX(T.CREATE_TIME) WHEN NULL THEN TO_DATE('2019-03-05 00:00:01','yyyy-MM-dd hh ...

  4. Mysql报错java.sql.SQLException:null,message from server:"Host '27,45,38,132' is not allowed to connect

    Mysql报错java.sql.SQLException:null,message from server:"Host '27,45,38,132' is not allowed to co ...

  5. Mysql CASE WHEN 用法

    select sum(1) as col_0_0_, sum(case vciinfo.useable when -1 then 1 else 0 end) as col_1_0_, sum(case ...

  6. 阳性比例 mysql CASE UNION ALL

    阳性比例 mysql CASE UNION ALL SELECT t.*,t.type_0/all_ FROM ( SELECT FROM_UNIXTIME(create_time,'%Y-%m-%d ...

  7. mysql 查询出的数组为null怎么转换成0

    mysql 查询出的数组为null怎么转换成0 IFNULL(b.dayPay,0) as yesterdayPay,

  8. 【杂记】mysql 左右连接查询中的NULL的数据筛选问题,查询NULL设置默认值,DATE_FORMAT函数

    MySQL左右连接查询中的NULL的数据筛选问题 xpression 为 Null,则 IsNull 将返回 True:否则 IsNull 将返回 False. 如果 expression 由多个变量 ...

  9. mysql设置text字段为not null,并且没有默认值,插入报错:doesn't have a default value

    一.问题描述 在往数据库写入数据的时候,报错: '字段名' doesn't have a default value 本来这个错误是经常见到的,无非就是字段没有设置默认值造成的.奇怪的是,我这边报错的 ...

  10. mysql中,一个数字加上null,结果为null

    在mysql中,一个数字加上null,结果为null. 这个问题是我用update语句时遇见的,就像下边的例子 update tableName set number = number + x 这里的 ...

随机推荐

  1. SpringBoot入门-Redis(六)

    依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spri ...

  2. 复制centos 后不能上网,处理办法

    CentOS7 Failed to start LSB: Bring up/down解决方法 https://blog.51cto.com/addam/1839518 刚刚装好的虚拟机突然不能上网了, ...

  3. 一份ChatBot开源工程介绍(H5 + WX + KOA)

    vue-mpvue-ChatRobot https://github.com/fanqingsong/vue-mpvue-ChatRobot 前端 : Vue + Mpvue(支持移动端与小程序) ; ...

  4. SVM – 回归

    SVM的算法是很versatile的,在回归领域SVM同样十分出色的.而且和SVC类似,SVR的原理也是基于支持向量(来绘制辅助线),只不过在分类领域,支持向量是最靠近超平面的点,在回归领域,支持向量 ...

  5. 人脸识别(基于ArcFace)

    我们先来看看效果 上面是根据图片检测出其中的人脸.每个人脸的年龄还有性别,非常强大 第一步: 登录https://ai.arcsoft.com.cn/,注册开发者账号,身份认证,注册应用,得到APPI ...

  6. Windows SVN迁移到Linux 服务器

    一.备份VisualSVN项目 1. 现在要使用Linux作为svn服务器,之前是在windows Server 2008上的,用的是VisualSVN, 如下图所示. 2. 现在svn中有一个项目f ...

  7. linux 下core文件生成、路径、格式设置及调试

    core文件生成及调试1 代码 #include<stdio.h> int main() { int *p = NULL; *p = 0; return 0; } 2 在当前shell执行 ...

  8. WeQuant教程—1.2 从简单的量化系统开始

    你大概知道量化的思想最早在古巴比伦人计算行星轨迹的时候就已经诞生(算术运算),后来借助古希腊的形式化逻辑的发展,人们日益能从量化的思想中提炼和描述自然规律并运用到生产之中.不过,基于量化的思想打造一个 ...

  9. Overview to “Toon/Cel shading”

    转自:https://blog.felixkate.net/2017/01/19/toon-shading/ For the last couple of weeks I often had disc ...

  10. IDEA的java源码文件左边有一个红色的J

    解决办法: 如果源码文件这里已经有一个路径,那就添加现在的.java文件所在目录,或者删除了再重新添加