事故回溯

某业务流程操作为:

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项目中的全局异常处理器 Failed to invoke @ExceptionHandler method

    文件下载代码 @RequestMapping(value = { "/data/docking/picture/{id}/{empi}" }) public JsonApi pic ...

  2. 微信支付:URL未注册问题

    起因:一个项目已经做好了,微信支付也调通的,域名 www.xxxx.com ,某天客户需要换域名,改为weixin.xxxx.com, 原先的www转向客户自己的官网,结果换了之后,发现微信支付出错: ...

  3. (转nginx不浏览直接下载文件的解决方法

    原文:https://www.zhan200.com/xwt/39.html 如果nginx配置不对,就会造成部分文件,在浏览器中不是直接预览,而是进行了下载.修改的方法是修改配置文件.具体解决方法如 ...

  4. MSYS 工具和TOOLCHAIN的配置

    博客转载:https://blog.csdn.net/hunterhuang2013/article/details/70880569 1. MSYS2 自带的开发环境,安装的包叫 msys2-dev ...

  5. 移动端Web框架

    [参考]移动端 UI设计尺寸(一)篇 [参考]移动端界面设计之尺寸篇 [参考]介绍几个移动web app开发框架 [参考]移动webapp前端ui用哪个框架好 H5 做移动开发也分两种,一种就是正常的 ...

  6. GhostVLAD for set-based face recognition

    GhostVLAD for set-based face recognition 中提到了文章解决的是template-based face recognition. VLAD: vector of ...

  7. Centos7时区修改方法汇总

    方法一: timedatectl set-timezone Asia/Shanghai 方法二: 设置环境变量TZ(这个方法用得比较少,但是有一次就是这个方法帮了我大忙,其他都无法实现修改时区,特此记 ...

  8. 1-4dockerfile基本使用

    1.创建一个文件夹 mkdir mynginx cd mynginx touch Dockerfile [root@VM_0_10_centos mynginx]# cat Dockerfile FR ...

  9. [ARM-LInux开发]linux设备驱动makefile入门解析

    以下内容仅作参考,能力有限,如有错误还请纠正.对于一个普通的linux设备驱动模块,以下是一个经典的makefile代码,使用下面这个makefile可以完成大部分驱动的编译,使用时只需要修改一下要编 ...

  10. Java基础知识点总结(二)

    “https://www.runoob.com/java” 1.Java运算符优先级 2.内置数据类型的包装类 抽象类Number是所有包装类(Integer.Long.Byte.Double.Flo ...