一、问题描述
一个简单地建表语句,去掉了部分敏感字段
CREATE TABLE `test1` (
`start_date` timestamp NOT NULL COMMENT '同步开始时间',
`end_date` timestamp NOT NULL COMMENT '同步结束时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
放在navicat执行直接报错:
"1067: Invalid default value for 'end_date'"
二、问题分析
1、sql_mode直接导致报错
1、执行sql语句查询一下sql_mode
select @@sql_mode;
STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE #当有NO_ZERO_DATE时引起这个报错
具体可看附录
2、报错原因
`end_date` timestamp NOT NULL COMMENT '同步结束时间';
TIMESTAMP列(如果未声明为NULL或显示DEFAULT子句)将自动分配DEFAULT ‘0000-00-00 00:00:00′(零时间戳)
这不满足sql_mode中的NO_ZERO_DATE而报错
2、显示第二列timestamp报错
1、疑问
新建表两列都是timestamp数据类型,并且约束条件一致,为啥报错只报第二列的
2、创建表进行测试
CREATE TABLE `test2` (
`start_date` timestamp NOT NULL COMMENT '同步开始时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3、查看建表语句
show create table test2;
CREATE TABLE `test2` (
`start_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '同步开始时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
4、总结
表中的第一个TIMESTAMP列(如果未声明为NULL或显示DEFAULT或ON UPDATE子句)
将自动分配DEFAULT CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMESTAMP属性
第一个之后的TIMESTAMP列(如果未声明为NULL或显示DEFAULT子句)将自动分配DEFAULT ‘0000-00-00 00:00:00′(零时间戳),
这不满足sql_mode中的NO_ZERO_DATE而报错。
三、问题解决
1、修改sql_mode
1、查询现在的sql_mode
select @@sql_mode;
比如:
STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE
2、暂时解决方案,可以在该会话(session)中暂时修改其sql_mode参数,但是会话结束,下一次会话,修改的参数就失效,变为原来的参数。
set SQL_MODE = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE';
执行完之后就可以继续执行建表语句,但是此方法只在当前会话生效
3、永久解决方案,可以通过执行sql方式,设置全局sql_mode参数,设置后效果永久生效。
set GLOBAL sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE';
此方法在当前服务生效,重启失效
4、永久解决方案,直接修改配置文件,
有sql_mode的设置就修改,没有就添加sql_mode
[msyqld]
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE
然后重启mysql
5、总结
修改sql_mode能解决,但是不推荐
2、更改参数explicit_defaults_for_timestamp
[mysqld]
explicit_defaults_for_timestamp=1
然后重启mysql
业务机器一般不允许重启,这种办法也不推荐
此处不多解释,另开一篇文章介绍explicit_defaults_for_timestamp,并且explicit_defaults_for_timestamp参数可以在线修改
3、更改表约束
1、增加null default null
`end_date` timestamp NULL DEFAULT NULL COMMENT '同步结束时间'
2、模仿第一个TIMESTAMP列自动分配
`end_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '同步结束时间'
或者
`end_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '同步结束时间' COMMENT '同步结束时间'
四、附录:
1、NO_ZERO_IN_DATE:
在严格模式,不接受月或日部分为0的日期。如果使用IGNORE选项,我们为类似的日期插入'0000-00-00'。
在非严格模式,可以接受该日期,但会生成警告。
2、NO_ZERO_DATE:
在严格模式,不要将 '0000-00-00'做为合法日期。你仍然可以用IGNORE选项插入零日期。
在非严格模式,可以接受该日期,但会生成警告
3、区别
NO_ZERO_IN_DATE 支持 0000-00-00 0000-01-01 (年月日都为0,月日都不为0)插入表
NO_ZERO_DATE 支持 1000-00-00 0000-01-00 0000-00-01(年月日中任何一个不为0)插入表,年月日全为0报错
NO_ZERO_IN_DATE、NO_ZERO_DATE 同时设置 支持 0000-01-01 1000-01-01(只允许年为0)插入表