前言:

前面文章我们介绍过日期和时间字段的查询方法,最近遇到日期值为零的问题。原来了解过和 sql_mode 参数设置有关,但还不是特别清楚,本篇文章将探究下MySQL怎么处理日期值为零的问题。

1.问题描述

这里我们说的日期为零值是指年、月、日为零,即'0000-00-00'。显然,这是不合法的日期值,但由于设计问题或历史遗留问题,有时候数据库中有类似日期值为零的数据,默认情况下插入零值日期会报错,可以通过修改参数sql_mode模式来避免该问题。下面展示下默认情况下插入零值的情况:

# 首先创建测试表
CREATE TABLE `t_zerodate` (
`increment_id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`year_col` year DEFAULT NULL COMMENT '年',
`date_col` date DEFAULT NULL COMMENT '日期',
`dt_col` datetime DEFAULT NULL COMMENT 'datetime时间',
`ts_col` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'timestamp时间',
PRIMARY KEY (`increment_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='zerodate'; # 查看sql_mode模式
mysql> select @@sql_mode;
+----------------------------------------------------------------------------------------------------+
| @@sql_mode |
+----------------------------------------------------------------------------------------------------+
| STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION |
+----------------------------------------------------------------------------------------------------+ # 分别插入年、月、日为零值测试
# 年份为0000 插入成功
mysql> insert into t_zerodate (year_col) values (0000);
Query OK, 1 row affected (0.02 sec) # 月、日都不为零时 可插入成功
mysql> insert into t_zerodate (date_col) values ('0000-00-00');
ERROR 1292 (22007): Incorrect date value: '0000-00-00' for column 'date_col' at row 1
mysql> insert into t_zerodate (date_col) values ('0000-01-00');
ERROR 1292 (22007): Incorrect date value: '0000-01-00' for column 'date_col' at row 1
mysql> insert into t_zerodate (date_col) values ('0000-00-01');
ERROR 1292 (22007): Incorrect date value: '0000-00-01' for column 'date_col' at row 1
mysql> insert into t_zerodate (date_col) values ('0000-01-01');
Query OK, 1 row affected (0.02 sec)

2.sql_mode变更测试

关于sql_mode,原来写过一篇文章,sql_mode支持多个变量的不同组合,不同的sql_mode影响服务端支持的SQL语法以及数据校验规则。其中 NO_ZERO_IN_DATENO_ZERO_DATE这两个变量影响MySQL对日期零值的处理。上面测试中可以发现,严格模式下,当sql_mode中包含NO_ZERO_IN_DATE,NO_ZERO_DATE两个变量时,月和日都不为零时可以插入成功。

乍一看,NO_ZERO_IN_DATE和NO_ZERO_DATE两个变量很相似,但作用有什么不同呢?下面我们给出这两个变量的作用并做下具体测试。

NO_ZERO_DATE模式影响服务端是否允许将 '0000-00-00' 作为有效日期。其效果还取决于sql_mode是否启用了严格模式。

  • 如果未启用此模式,'0000-00-00'则允许插入并且不会产生警告。
  • 如果只启用此模式,'0000-00-00'则允许插入但是会产生警告。
  • 如果启用了此模式和严格模式,'0000-00-00'则会被认定为非法,并且插入也会产生错误。除非同时带有IGNORE,对于 INSERT IGNORE和UPDATE IGNORE,'0000-00-00'则允许插入但是会产生警告。

NO_ZERO_IN_DATE模式影响服务端是否允许插入年份部分非零但月或日部分为0的日期。(例如'2010-00-01'或 '2010-01-00',但不影响日期'0000-00-00'),其效果同样还取决于sql_mode是否启用了严格模式。

  • 如果未启用此模式,则允许部分为零的日期插入,并且不会产生任何警告。
  • 如果只启用此模式,则将该零值日期插入为'0000-00-00'并产生警告。
  • 如果启用了此模式和严格模式,则除非IGNORE同时指定,否则不允许插入为零的日期。对于INSERT IGNORE和 UPDATE IGNORE,将该零值日期插入为'0000-00-00'并产生警告。

同时,官方文档中指出:NO_ZERO_DATE和NO_ZERO_IN_DATE虽然不是严格模式的一部分,但应与严格模式结合使用,如果在未启用严格模式的情况下启用了NO_ZERO_DATE或NO_ZERO_IN_DATE则会产生警告,反之亦然,(sql_mode中包含STRICT_TRANS_TABLES,一般可认为启用了严格模式)。

下面我们来测试下,严格模式下分别启用和不启用这两个变量的效果:

1.严格模式下 同时启用NO_ZERO_DATE、NO_ZERO_IN_DATE

# 上文已经测试过,正常情况下 月、日都不为零时 可插入成功
# 下面测试insert ignore into
mysql> set session sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION";
Query OK, 0 rows affected (0.00 sec) mysql> truncate table t_zerodate;
Query OK, 0 rows affected (0.07 sec) mysql> insert ignore into t_zerodate (date_col) values ('0000-00-00');
Query OK, 1 row affected, 1 warning (0.01 sec) mysql> insert ignore into t_zerodate (date_col) values ('2010-00-00');
Query OK, 1 row affected, 1 warning (0.01 sec) mysql> insert ignore into t_zerodate (date_col) values ('2010-01-00');
Query OK, 1 row affected, 1 warning (0.01 sec) mysql> insert ignore into t_zerodate (date_col) values ('2010-00-01');
Query OK, 1 row affected, 1 warning (0.01 sec) mysql> select date_col from t_zerodate;
+------------+
| date_col |
+------------+
| 0000-00-00 |
| 0000-00-00 |
| 0000-00-00 |
| 0000-00-00 |
+------------+ # 结论:
正常情况下 月、日都不为零时 可插入成功;
对于INSERT IGNORE则允许插入,但会产生告警,并会将零值日期保存为'0000-00-00'。 2.严格模式下 不启用NO_ZERO_DATE mysql> set session sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION";
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> truncate table t_zerodate;
Query OK, 0 rows affected (0.08 sec) mysql> insert into t_zerodate (date_col) values ('0000-00-00');
Query OK, 1 row affected (0.01 sec) mysql> insert into t_zerodate (date_col) values ('2010-00-01');
ERROR 1292 (22007): Incorrect date value: '2010-00-01' for column 'date_col' at row 1
mysql> insert into t_zerodate (date_col) values ('2010-01-00');
ERROR 1292 (22007): Incorrect date value: '2010-01-00' for column 'date_col' at row 1
mysql> insert into t_zerodate (date_col) values ('0000-00-01');
ERROR 1292 (22007): Incorrect date value: '0000-00-01' for column 'date_col' at row 1
mysql> insert into t_zerodate (date_col) values ('0000-01-00');
ERROR 1292 (22007): Incorrect date value: '0000-01-00' for column 'date_col' at row 1 # 结论:
'0000-00-00'可以正常插入,其余月、日为零的日期插入会报错;
对于INSERT IGNORE则允许插入,但会产生告警,并会将零值日期保存为'0000-00-00'。 3.严格模式下 不启用NO_ZERO_IN_DATE mysql> set session sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION";
Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> truncate table t_zerodate;
Query OK, 0 rows affected (0.07 sec) mysql> insert into t_zerodate (date_col) values ('0000-00-00');
ERROR 1292 (22007): Incorrect date value: '0000-00-00' for column 'date_col' at row 1
mysql> insert into t_zerodate (date_col) values ('2010-00-00');
Query OK, 1 row affected (0.01 sec) mysql> insert into t_zerodate (date_col) values ('2010-00-01');
Query OK, 1 row affected (0.00 sec) mysql> insert into t_zerodate (date_col) values ('2010-01-00');
Query OK, 1 row affected (0.00 sec) mysql> insert ignore into t_zerodate (date_col) values ('0000-00-00');
Query OK, 1 row affected, 1 warning (0.01 sec) mysql> select date_col from t_zerodate;
+------------+
| date_col |
+------------+
| 2010-00-00 |
| 2010-00-01 |
| 2010-01-00 |
| 0000-00-00 |
+------------+ # 结论:
'0000-00-00'插入报错,其余月、日为零的日期可以正常插入;
对于INSERT IGNORE则允许插入'0000-00-00',但会产生告警。 4.严格模式下 NO_ZERO_DATE和NO_ZERO_IN_DATE都不启用 mysql> set session sql_mode = "STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION";
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> truncate table t_zerodate;
Query OK, 0 rows affected (0.09 sec) mysql> insert into t_zerodate (date_col) values ('0000-00-00');
Query OK, 1 row affected (0.01 sec) mysql> insert into t_zerodate (date_col) values ('2010-00-00');
Query OK, 1 row affected (0.01 sec) mysql> insert into t_zerodate (date_col) values ('2010-00-01');
Query OK, 1 row affected (0.01 sec) mysql> insert into t_zerodate (date_col) values ('2010-01-00');
Query OK, 1 row affected (0.01 sec) # 结论:
'0000-00-00'和其余月、日为零的日期都可以正常插入。

3.结论及建议

简单总结下,NO_ZERO_DATE模式影响'0000-00-00'日期的插入,NO_ZERO_IN_DATE模式影响除'0000-00-00'外的月、日为零的日期的插入。另外无论何种模式,YEAR类型都允许0000插入,这两个变量影响的是DATE、DATETIME、TIMESTAMP三种字段类型中对日期部分为零的处理。

至于我们是否要启用这两种模式,这取决于业务需求。如果你的业务有插入零值日期的需求,则可以选择sql_mode中不要包含NO_ZERO_DATE和NO_ZERO_IN_DATE,例如,某字段要求设置为DATE类型且不为空,默认值设为'0000-00-00'。一般情况下,NO_ZERO_DATE和NO_ZERO_IN_DATE建议同时有或者同时没有,有插入零日期值的需求则可以去除二者,没有此类需要则可以保留二者。这里提醒下,官方文档中讲到,这两个变量在未来版本中不再作为独立变量使用,故官方不推荐使用。

总结:

写了这么多,不知道你认真看了多少,其实本篇文章讲的东西还是比较简单的。如果你遇到过此类问题,再看下本篇文章可能理解会更深刻些,没遇过此类问题的小伙伴,希望这篇文章可以让你知道MySQL对于零值日期有不同的处理。

MySQL关于日期为零值的处理的更多相关文章

  1. mysql日期类型默认值'0000-00-00'容错处理

    mysql日期默认值'0000-00-00'惹的祸 .net连mysql数据库时,如果表里有字段是日期型且值是‘0000-00-00’时,会报错.在C#里面日期不可能是那样的.或许是最小日期定义的差别 ...

  2. MySQL之日期时间类型

    mysql(5.5)所支持的日期时间类型有:DATETIME. TIMESTAMP.DATE.TIME.YEAR. 几种类型比较如下: 日期时间类型 占用空间 日期格式 最小值 最大值 零值表示  D ...

  3. MySQL计算日期的函数DATE_SUB(d,INTERVAL expr type)

    MySQL计算日期的函数DATE_SUB(d,INTERVAL expr type) DATE_SUB(d,INTERVAL expr type)函数返回起始日期d减去一个时间段后的日期. expr是 ...

  4. MySQL:日期函数、时间函数总结

    MySQL 获得当前日期时间 函数 获得当前日期+时间(date + time)函数:now() mysql> select now(); +---------------------+ | n ...

  5. mysql 关于日期时间的字段类型

    mysql日期和时间类型 mysql有5种表示时间值的日期和时间类型,分别为.DATE,TIME,YEAR,DATETIME,TIMESTAMP. TIMESTAMP类型有专有的自动更新特性, TIM ...

  6. MySQL数据类型--日期时间

    一.博客前言 自接触学习MySQL已有一段时间了,对于MySQL的基础知识还是略懂略懂的.在这一路学习过来,每次不管看书还是网上看的资料,对于MySQL数据类型中的时间日期类型总是一扫而过,不曾停下来 ...

  7. MySQL:日期函数、时间函数总结(MySQL 5.X)

    http://www.cnblogs.com/she27/archive/2009/01/16/1377089.html 原文:http://www.51sdj.com/phpcms/picture/ ...

  8. 【转】MySQL:日期函数、时间函数总结(MySQL 5.X)

    转自:http://www.cnblogs.com/she27/articles/1377089.html 一.MySQL 获得当前日期时间 函数1.1 获得当前日期+时间(date + time)函 ...

  9. mysql时间日期函数总结(转)

    DAYOFWEEK(date)  返回日期date是星期几(1=星期天,2=星期一,……7=星期六,ODBC标准)mysql> select DAYOFWEEK('1998-02-03');  ...

随机推荐

  1. 阿里二面:什么是mmap?

    平时在面试中你肯定会经常碰见的问题就是:RocketMQ为什么快?Kafka为什么快?什么是mmap? 这一类的问题都逃不过的一个点就是零拷贝,虽然还有一些其他的原因,但是今天我们的话题主要就是零拷贝 ...

  2. ArrayList这篇就够了

    提起ArrayList,相信很多小伙伴都用过,而且还不少用.但在几年之前,我在一场面试中,面试官要求说出ArrayList的扩容机制.很显然,那个时候的我并没有关注这些,从而错过了一次机会.不过好在我 ...

  3. 前端vue使用高德地图

    首先,注册Key 1.注册开发者账号,成为高德开放平台开发者 2.登陆之后,在进入「应用管理」 页面「创建新应用」 3.为应用添加 Key,「服务平台」一项请选择「 Web 端 ( JSAPI ) 」 ...

  4. 彻底搞清Flink中的Window

    窗口 在流处理应用中,数据是连续不断的,因此我们不可能等到所有数据都到了才开始处理.当然我们可以每来一个消息就处理一次,但是有时我们需要做一些聚合类的处理,例如:在过去的1分钟内有多少用户点击了我们的 ...

  5. KMP字符串匹配学习笔记

    部分内容引自皎月半洒花的博客 模式串匹配问题模型给定一个需要处理的文本串和一个需要在文本串中搜索的模式串,查询在该文本串中,给出的模式串的出现有无.次数.位置等.算法思想每次失配之后不会从头开始枚举, ...

  6. 生产环境中mysql数据库由主从关系切换为主主关系

    目录 一.清除原从数据库数据及主从关系 1.1.关闭主从数据库原有的主从关系 1.2.清除从数据库原有数据 二.将主库上的数据备份到从库 2.1.备份主库数据到从库 2.2.在从库使用tsc.sql文 ...

  7. golang 入门之环境搭建

    [安装运行时] 直接上官网下载运行时安装即可:golang 官网 根据自己操作系统选择 golang 运行时版本,例如windows 当然我们也可以直接选择下面的稳定版本进行下载 下载安装后,安装程序 ...

  8. day-05-字典

    字典的初识 why: 列表可以存储大量的数据,但数据之间的关联性不强 列表的查询速度比较慢.数量越大查询越慢 what:容器型数据类型:dict how: 数据类型的分类(可变与不可变) 可变(不可哈 ...

  9. ES9的新特性:异步遍历Async iteration

    ES9的新特性:异步遍历Async iteration 目录 简介 异步遍历 异步iterable的遍历 异步iterable的生成 异步方法和异步生成器 简介 在ES6中,引入了同步iteratio ...

  10. 如何用 Electron + WebRTC 开发一个跨平台的视频会议应用

    在搭建在线教育.医疗.视频会议等场景时,很多中小型公司常常面临 PC 客户端和 Web 端二选一的抉择.Electron 技术的出现解决了这一难题,只需前端开发就能完成一个跨平台的 PC 端应用.本文 ...