MySQL的TIMESTAMP类型字段非空和默认值属性的影响

同事说他通过某款商业数据同步软件将一个 MySQL 5.7.28 的库同步到 MySQL 5.7.20 的库时,如果表中含有 TIMESTAMP 数据类型、缺省值为 current_timestamp 的字段,这些表的同步任务就都失败了,而另外的一些包含了 DATETIME 数据类型的表就同步成功了,不知道这是不是 MySQL 版本差异导致的?

不通过软件,直接手工创建,不会报错,模拟的 SQL ,如下所示,一个主键 id ,外加两个 timestamp类型的字段,都设置了默认值:

sql 复制代码
create table test(
id int not null auto_increment,
createtime timestamp null default current_timestamp,
updatetime timestamp null default current_timestamp on update current_timestamp
);

同步软件报错的日志如下,提示为字段 updatetime 设置了无效的默认值

bash 复制代码
ERROR_GENERAL "Handling new table 'test'.'flow' failed
execute create table statement failed, statement CREATE TABLE `test`.`test` ( `id` INT
NOT NULL, `createtime` TIMESTAMP, `updatetime` TIMESTAMP ) COLLATE utf8_general_ci
[MySQL][ODBC 5.3(w) Driver][mysqld-5.7.20-log]Invalid default value 

细致的老司机们,可能会注意到,日志中记录的 SQL 语句显示 createtimeupdatetime 都只声明了
TIMESTAMP 类型,缺少了原始建表语句中的 NULL 和 DEFAULT 属性,这会有什么影响呢?

MySQL 中有个explicit_defaults_for_timestamp 变量,他决定了 MySQL 是否为 TIMESTAMP 列的默认值和 NULL 值的处理启用某些非标准的行为:

如文档所说,如果 explicit_defaults_for_timestamp=OFF ,服务器会启用非标准行为,并按以下规则处理TIMESTAMP 列:

(1) 没有明确使用 NULL 属性声明的 TIMESTAMP 列会自动使用 NOT NULL 属性声明。给这样的列分

配一个 NULL 的值是允许的,并将该列设置为 current timestamp 。

(2) 表中的第一个 TIMESTAMP 列,如果没有明确地用 NULL 属性,DEFAULT 属性或 ON UPDATE

属性声明,将自动用 DEFAULT CURRENT_TIMESTAMP 和 ON UPDATE CURRENT_TIMESTAMP 属性声明。

(3) 在第一个列之后的 TIMESTAMP 列,如果没有明确地用 NULL 属性或明确的 DEFAULT 属性来声

明,就会自动声明为 DEFAULT '0000-00-00 00:00:00' 。对于插入的行,如果没有为该列指定明确的值,那么该列将被分配为 '0000-00-00 00:00:00' ,并且不会发生警告。根据是否启用了严格的 SQL mode 或包含 NO_ZERO_DATE 的 SQL mode ,默认值 '0000-00-00 00:00:00' 可能是不被允许的。

目标库 MySQL 5.7.20 的 explicit_defaults_for_timestamp 值是默认的 OFF ,结合上述规则,就可以模拟复现上述问题了。

虽然原始的建表语句中 TIMESTAMP 类型字段包含了 NULL 和 DEFAULT 属性,但是根据同步软件的

错误提示,很明显,不知道什么原因,他在执行的时候忽略了这两个属性,导致真实的执行语句是:

sql 复制代码
create table test(id int not null,createtime timestamp,updatetime timestamp);

我们在客户端,执行上述 SQL ,就会得到相同的错误信息:

sql 复制代码
Invalid default value 

建表语句第一个 TIMESTAMP 类型字段是 createtime ,按照上述规则(1),没有明确使用 NULL 属性声明的 TIMESTAMP 列会自动使用 NOT NULL 属性声明,按照上述规则(2),表中的第一个 TIMESTAMP列,如果没有明确地用 NULL 属性,DEFAULT 属性或 ON UPDATE 属性声明,将自动用 DEFAULTCURRENT_TIMESTAMP 和 ON UPDATE CURRENT_TIMESTAMP 属性声明,假设这个建表语句中只有一个 TIMESTAMP 类型字段 createtime ,他的结构会改成

按照上述规则(3),在第一个列之后的 TIMESTAMP 列,即此处的 updatetime ,如果没有明确地用

NULL 属性或明确的 DEFAULT 属性来声明,就会自动声明为 DEFAULT '0000-00-00 00:00:00',但是这个 MySQL 5.7.20 的 sql_mode 变量包含了 NO_ZERO_DATE 规则,因此默认值'0000-00-00 00:00:00'不被允许,所以提示了错误

我们能推断,如果表中存在两个及以上这种情况的 TIMESTAMP 类型字段,通过这个软件做同步,建表的时候,就会报错,这个可能是软件的一个 bug ,或者存在其他的配置控制,但就不在数据库的范畴了。

另外,多说一点,原始语句中 createtime 和 updatetime 列都指定了默认值,但还是设置 NULL 属性,这其实就有些矛盾了,或者说是设计上的不严谨,从规范设计开发的角度,还是应该避免的:

sql 复制代码
create table test(
id int not null auto_increment,
createtime timestamp null default current_timestamp,
updatetime timestamp null default current_timestamp on update current_timestamp
);
相关推荐
JM_life9 分钟前
Python入门系列之九-数据分析与可视化
数据库·python·数据分析
欣慰的三叶草(● ̄(エ) ̄●)36 分钟前
Navicat 17 for Mac 数据库管理软件
数据库·macos·数据库管理·navicat·数据库连接工具·navicat17·mysql连接
Greyscarf1 小时前
SQL Server 数据库 忘记密码
数据库
森森淼淼丶1 小时前
oceanbase集群访问异常问题处理
运维·数据库·oceanbase
阿年、嗯啊1 小时前
MySQL和Hive中的行转列、列转行
数据库·hive·mysql·侧窗·行专列、列转行·hive侧窗列转行·构造map数据结构
tatasix1 小时前
Redis 实现分布式锁
数据库·redis·分布式
高铭杰2 小时前
Postgresql中clog与xid对应关系计算方法(速查表)
数据库·postgresql·clog·xid
鸠摩智首席音效师2 小时前
如何备份和恢复 PostgreSQL 数据库 ?
数据库·postgresql
码农君莫笑2 小时前
SQL中聚类后字段数据串联字符串方法研究
数据库·sql
幽兰的天空2 小时前
在C#中,如何使用委托实现事件处理?
前端·数据库·c#