事故回溯

某业务流程操作为:

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. 使用evenlet包实现 concurrent.futures.executor包的鸭子类

    适配成同一个同样的公有方法. # -*- coding: utf-8 -*- # @Author : ydf # @Time : 2019/7/3 10:35 import time import w ...

  2. fastreport 条形码 宽度问题

    fastreport 的barcode 如果不设置AutoSize 确实可以控制宽度 但是生成后 基本没办法扫 所以换个思路 直接等比缩小 设置里面的zoom 比例为0.8  针对20位左右的条形码就 ...

  3. flink ---- 系统内部消息传递的exactly once语义

    At Most once,At Least once和Exactly once 在分布式系统中,组成系统的各个计算机是独立的.这些计算机有可能fail. 一个sender发送一条message到rec ...

  4. 【Docker学习之三】Docker查找拉取镜像、启动容器、容器使用

    环境 docker-ce-19.03.1-3.el7.x86_64 CentOS 7 一.查找.拉取镜像.启动容器1.查找镜像-docker search默认查找Docker Hub上的镜像,举例:D ...

  5. Winograd Convolution 推导 - 从1D到2D

    Winograd Convolution 推导 - 从1D到2D 姚伟峰 http://www.cnblogs.com/Matrix_Yao/ Winograd Convolution 推导 - 从1 ...

  6. 腾讯物联网操作系统正式开源,最小体积仅1.8 KB

    9月18日,腾讯宣布将开源自主研发的轻量级物联网实时操作系统TencentOS tiny.相比市场上其它系统,腾讯TencentOS tiny在资源占用.设备成本.功耗管理以及安全稳定等层面极具竞争力 ...

  7. 17 SpringMVC response响应

    1.Model.ModelMap和ModelAndView的使用详解 Spring-MVC在请求处理方法可出现和返回的参数类型中,最重要就是Model和ModelAndView了,对于MVC框架,控制 ...

  8. GitHub: Oracle Database on Docker 为测试 改天试试

    Oracle Database on Docker https://github.com/oracle/docker-images/tree/master/OracleDatabase/SingleI ...

  9. vs 远程调试

    程序在vs安装目录: D:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\Remote Debugge ...

  10. 嵌入式02 STM32 实验09 独立/窗口看门狗

    一.独立看门狗和窗口看门狗 看门狗:单片机系统在外界的干扰下会出现程序跑飞的现象导致死循环,或者崩溃,看门狗电路就是为了避免这种情况的发生,看门狗的作用就是在一定的事件内(通过计数器实现)若没有收到喂 ...