【问题标题】:MySQL Incorrect datetime value: '0000-00-00 00:00:00'MySQL 不正确的日期时间值:'0000-00-00 00:00:00'
【发布时间】:2016-06-04 13:22:41
【问题描述】:

我最近接手了一个 10 年前创建的旧项目。它使用 MySQL 5.1。

除此之外,我需要将默认字符集从 latin1 更改为 utf8。

例如,我有这样的表:

  CREATE TABLE `users` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `first_name` varchar(45) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL,
    `last_name` varchar(45) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL,
    `username` varchar(127) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
    `email` varchar(127) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
    `pass` varchar(20) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
    `active` char(1) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL DEFAULT 'Y',
    `created` datetime NOT NULL,
    `last_login` datetime DEFAULT NULL,
    `author` varchar(1) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT 'N',
    `locked_at` datetime DEFAULT NULL,
    `created_at` datetime DEFAULT NULL,
    `updated_at` datetime DEFAULT NULL,
    `ripple_token` varchar(36) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL,
    `ripple_token_expires` datetime DEFAULT '2014-10-31 08:03:55',
    `authentication_token` varchar(255) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `index_users_on_reset_password_token` (`reset_password_token`),
    UNIQUE KEY `index_users_on_confirmation_token` (`confirmation_token`),
    UNIQUE KEY `index_users_on_unlock_token` (`unlock_token`),
    KEY `users_active` (`active`),
    KEY `users_username` (`username`),
    KEY `index_users_on_email` (`email`)
  ) ENGINE=InnoDB AUTO_INCREMENT=1677 DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC

我设置了自己的 Mac 来解决这个问题。没有想太多,我运行了安装 MySQL 5.7 的“brew install mysql”。所以我有一些版本冲突。

我下载了该数据库的副本并将其导入。

如果我尝试运行这样的查询:

  ALTER TABLE users MODIFY first_name varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci    NOT NULL  

我得到这个错误:

  ERROR 1292 (22007): Incorrect datetime value: '0000-00-00 00:00:00' for column 'created' at row 1

我想我可以解决这个问题:

  ALTER TABLE users MODIFY created datetime  NULL DEFAULT '1970-01-01 00:00:00';
  Query OK, 0 rows affected (0.06 sec)
  Records: 0  Duplicates: 0  Warnings: 0

但我明白了:

  ALTER TABLE users MODIFY first_name varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci    NOT NULL ;
  ERROR 1292 (22007): Incorrect datetime value: '0000-00-00 00:00:00' for column 'created' at row 1

我必须更新每个值吗?

【问题讨论】:

  • 接受的答案已过时,请参阅下面的@TariqKhan's answer for mysql 8.0

标签: mysql


【解决方案1】:

如果表为空或不是很大,我的建议是将创建语句导出为 .sql 文件,然后根据需要重写它们。如果您有任何现有数据,也可以这样做,即导出插入语句(我建议在单独的文件中作为创建语句执行此操作)。最后,删除表并执行第一个创建语句,然后插入。

您可以使用 mysqldump 命令,包含在您的 MySQL 安装中,或者您也可以安装 MySQL Workbench,这是一个免费的图形工具,它还以非常可定制的方式包含此选项,而无需查找特定命令选项。

【讨论】:

  • Lucia Pasarin,我非常喜欢你的想法,但是数据不会被截断吗?某些 UTF8 数据是否比 latin1 占用更多字节?如果以前适合 varchar 255 的东西,也许现在不适合?我是否应该将所有 varchars 更改为“文本”字段?
  • 是的,你是对的。这可能会发生,因为 latin1 每个字符使用 1 个字节,而 utf8 每个字符最多使用 4 个字节(取决于 MySQL 的版本和 utf8 dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8.html 的类型)。因此,您不一定需要 TEXT 类型。我会假设您以前存在的 x4 尺寸应该可以工作。
  • 当您想删除一个文件时,这相当于重新格式化您的硬盘驱动器的数据库。可以肯定地说,这种解决方案在生产环境中是不可接受的。
  • 下面评论中的答案是最好的 UPDATE users SET created = NULL WHERE created=0;
【解决方案2】:

使用ALTER TABLE 语句更改列的默认值,例如

 ALTER TABLE users MODIFY created datetime  NULL DEFAULT '1970-01-02'

... 不会更改任何已存储的值。 “默认”值适用于插入的行,并且没有为列提供值。


至于您遇到错误的原因,您会话的sql_mode 设置可能包含NO_ZERO_DATE

参考:http://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_no_zero_date

当您执行“导入”时,向该表执行 INSERT 的 SQL 语句在允许零日期的会话中运行。

查看 sql_mode 设置:

SHOW VARIABLES LIKE 'sql_mode' ;

-或-

SELECT @@sql_mode ;

至于如何“修复”当前的问题,让你在运行ALTER TABLE语句时不会抛出错误。

几个选项:

1) 通过删除 NO_ZERO_DATENO_ZERO_IN_DATEsql_mode 更改为允许零日期。更改可以应用到 my.cnf 文件中,因此重启 MySQL 服务器后,sql_mode 变量将被初始化为 my.cnf 中的设置。

对于临时更改,我们可以通过单个会话修改设置,而无需进行全局更改。

-- save current setting of sql_mode
SET @old_sql_mode := @@sql_mode ;

-- derive a new value by removing NO_ZERO_DATE and NO_ZERO_IN_DATE
SET @new_sql_mode := @old_sql_mode ;
SET @new_sql_mode := TRIM(BOTH ',' FROM REPLACE(CONCAT(',',@new_sql_mode,','),',NO_ZERO_DATE,'  ,','));
SET @new_sql_mode := TRIM(BOTH ',' FROM REPLACE(CONCAT(',',@new_sql_mode,','),',NO_ZERO_IN_DATE,',','));
SET @@sql_mode := @new_sql_mode ;

-- perform the operation that errors due to "zero dates"

-- when we are done with required operations, we can revert back
-- to the original sql_mode setting, from the value we saved
SET @@sql_mode := @old_sql_mode ;

2) 更改created 列以允许空值,并更新现有行以将零日期更改为空值

3) 更新现有行以将零日期更改为有效日期


我们不需要运行单独的语句来更新每一行。我们可以一口气更新所有的行(假设它是一个大小合理的表。对于一个更大的表,为了避免大量的回滚/撤消生成,我们可以在合理大小的块中执行操作。)

在问题中,为表定义显示的AUTO_INCREMENT 值向我们保证行数不会过多。

如果我们已经更改了 created 列以允许 NULL 值,我们可以执行以下操作:

UPDATE  `users` SET `created` = NULL WHERE `created` = '0000-00-00 00:00:00'

或者,我们可以将它们设置为有效日期,例如1970 年 1 月 2 日

UPDATE  `users` SET `created` = '1970-01-02' WHERE `created` = '0000-00-00 00:00:00'

(请注意,1970 年 1 月 1 日午夜的日期时间值 ('1970-01-01 00:00:00')“零日期”。这将被评估为 '0000-00-00 00:00:00'

【讨论】:

  • 是的,这对我有用。我在 my.ini 文件中搜索了 sql-mode 并删除了 NO_ZERO_IN_DATE 和 NO_ZERO_DATE 。然后重新启动服务。谢谢斯宾塞7593!
  • 当我执行 #2“更改创建的列以允许 NULL 值”时,它不会让我这样做,因为列值仍然会产生错误。卡住了。
  • @MikeWeir:大概是“won't let me”的意思是执行SQL语句时返回错误。这可能是由于sql_mode 的设置。较新版本的 MySQL 具有sql_mode 的默认设置,比以前的版本更严格。此处参考 8.0:dev.mysql.com/doc/refman/8.0/en/sql-mode.html 参见 NO_ZERO_DATEALLOW_INVALID_DATE 等。请注意,有些包含在 STRICT 模式中,例如STRICT_TRANS_TABLESSTRICT_ALL_TABLES等组合模式。要解决这些限制,请临时修改会话的 sql_mode,
  • @spencer7593 当然。我觉得那个选项也不是最好的。我接受了您设置一个非常旧的日期值(1970 年)的建议,我的系统将忽略它。感谢您提供的所有详细信息。
【解决方案3】:

我无法做到这一点:

UPDATE users SET created = NULL WHERE created = '0000-00-00 00:00:00'

(在 MySQL 5.7.13 上)。

我不断收到Incorrect datetime value: '0000-00-00 00:00:00' 错误。

奇怪的是,这有效:SELECT * FROM users WHERE created = '0000-00-00 00:00:00'。我不知道为什么前者失败而后者工作......也许是一个 MySQL 错误?

无论如何,这个 UPDATE 查询都有效:

UPDATE users SET created = NULL WHERE CAST(created AS CHAR(20)) = '0000-00-00 00:00:00'

【讨论】:

  • 我遇到了同样的问题,但是将最后一部分设置为 0 为我解决了这样的问题:UPDATE users SET created = NULL WHERE created = '0'
  • SELECT * FROM entity WHERE createdAt = "0000-00-00 00:00:00" 工作正常,但更新失败!我有同样的问题。用@obe CAST(created AS CHAR(20)) 的解决方案修复它......我认为这是一个错误。
  • 对我来说它就像UPDATE users SET created = NULL WHERE created=0(没有'大约为零)
  • 为了只替换没有时间戳的“0000-00-00”日期,我使用了 CHAR(11)
  • 那是纯粹的天才。为什么这不是公认的答案?对于仅没有时间戳的日期,最小值为 1000-01-01。考虑将其用作您打算留空或使用 0000-00-00 值的每个日期属性的默认值。
【解决方案4】:

我也有

SQLSTATE[22007]:日期时间格式无效:1292 日期时间不正确 值:列的“0000-00-00 00:00:00”

错误信息

通过将 0000-00-00 00:00:00 更改为 1970-01-01 08:00:00 来解决此问题

1970-01-01 08:00:00unix 时间戳为 0

【讨论】:

  • 问题是 OP 无法更改日期,因为错误。我猜这是来自NO_ZERO_DATE 的错误
【解决方案5】:

根据MySQL 5.7 Reference Manual

MySQL 5.7 中的默认 SQL 模式包括以下模式:ONLY_FULL_GROUP_BY、STRICT_TRANS_TABLES、NO_ZERO_IN_DATE、NO_ZERO_DATE、ERROR_FOR_DIVISION_BY_ZERO、NO_AUTO_CREATE_USER 和 NO_ENGINE_SUBSTITUTION。

由于0000-00-00 00:00:00 不是有效的DATETIME 值,因此您的数据库已损坏。这就是为什么 MySQL 5.7(默认启用 NO_ZERO_DATE 模式)在您尝试执行写入操作时会输出错误。

您可以修复您的表格,将所有无效值更新为任何其他有效值,例如NULL

UPDATE users SET created = NULL WHERE created < '0000-01-01 00:00:00'

另外,为避免此问题,我建议您始终将当前时间设置为created 类字段的默认值,以便它们自动填充到INSERT。做吧:

ALTER TABLE users
ALTER created SET DEFAULT CURRENT_TIMESTAMP

【讨论】:

    【解决方案6】:

    您可以将created字段的类型从datetime更改为varchar(255),然后您可以将所有值为"0000-00-00 00:00:00"的记录设置(更新)为NULL

    现在,您可以毫无错误地进行查询。 完成后,您可以将created字段的类型更改为datetime

    【讨论】:

      【解决方案7】:

      使sql模式不严格

      如果使用 laravel 进入 config->database,则进入 mysql 设置并设置严格模式为 false

      【讨论】:

      • 我可以在 myphpadmin 中执行此操作吗?
      • phpMyAdmin只是一个使用实际mysql服务器的接口,不管你用的是哪个接口,mysql命令不会随接口变化。试试这个命令 (set sql_mode='';) 或这个 (set global sql_mode='';) 来关闭它。
      • 是的,我发现关闭严格模式所需要做的就是在 my.cnf 底部添加 sql_mode=(之后什么都没有)
      【解决方案8】:
      SET sql_mode = 'NO_ZERO_DATE';
      UPDATE `news` SET `d_stop`='2038-01-01 00:00:00' WHERE `d_stop`='0000-00-00 00:00:00'
      

      【讨论】:

      • 这个答案将通过讨论为什么可以解决问题而得到改进。
      • 这会影响日期时间存储吗?或引起任何问题
      【解决方案9】:

      我在查询之前通过这样做修复了它

      SET SQL_MODE='ALLOW_INVALID_DATES';
      

      【讨论】:

      • 这是唯一的答案。用作:--init-command='SET SESSION FOREIGN_KEY_CHECKS=0;SET SQL_MODE='ALLOW_INVALID_DATES'
      • 这是 MySQL 8.0 中需要的,同样在/etc/mysql/conf.d/里面的对应文件中添加这个并从sql模式中删除NO_ZERO_IN_DATENO_ZERO_DATE
      • 我想再点击 UP 10 次!
      • 是的!救命的答案。我是这样使用的:mysql -u username -p database --init-command="SET SESSION FOREIGN_KEY_CHECKS=0;SET SQL_MODE='ALLOW_INVALID_DATES';" &lt; filename.sql
      【解决方案10】:

      在将 MySQL 从 5.6 升级到 5.7 后,我也遇到此错误

      我发现对我来说最好的解决方案是将这里的一些解决方案结合起来,并用最少的输入来实现它。

      我使用 MyPHPAdmin 是为了简化通过接口发送查询,因为这样我就可以轻松地检查结构和所有这些。您可以直接使用 ssh 或其他接口。无论如何,方法应该相似或相同。

      ...

      1.

      尝试修复数据库时首先检查实际错误:

      joomla.jos_menu 注意:旧格式的 TIME/TIMESTAMP/DATETIME 列已升级为新格式。

      警告:日期时间值不正确:第 1 行“checked_out_time”列的“0000-00-00 00:00:00”

      错误:“checked_out_time”的默认值无效

      状态:操作失败

      这告诉我 jos_menu 表中的checked_out_time 列需要修复所有错误日期以及更改“默认”日期。

      ...

      2.

      我根据错误消息中的信息运行 SQL 查询:

      UPDATE jos_menu SET checked_out_time = '1970-01-01 08:00:00' WHERE checked_out_time = 0
      

      如果您遇到错误,您可以使用下面的查询来代替它似乎总是有效的:

      UPDATE jos_menu SET checked_out_time = '1970-01-01 08:00:00' WHERE CAST(checked_out_time AS CHAR(20)) = '0000-00-00 00:00:00'
      

      ...

      3.

      完成后,我运行第二个 SQL 查询:

      ALTER TABLE `jos_menu` CHANGE `checked_out_time` `checked_out_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP;
      

      或者如果它是一个必须为NULL的日期

      ALTER TABLE `jos_menu` CHANGE `checked_out_time` `checked_out_time` DATETIME NULL DEFAULT NULL;
      

      ...

      如果我现在运行修复数据库,我会得到:

      joomla.jos_menu 确定

      ...

      工作得很好:)

      【讨论】:

        【解决方案11】:

        我遇到了类似的问题,但在我的情况下,某些行的值为 NULL。

        所以我首先更新表格:

        update `my_table`set modified = '1000-01-01 00:00:00' WHERE modified is null
        

        问题解决了,至少在我的情况下。

        【讨论】:

          【解决方案12】:

          这是我的解决方案PhpMyAdmin / Fedora 29 / MySQL 8.0(例如):

          set sql_mode='SOMETHING'; 不起作用,命令调用成功,但没有任何变化。

          set GLOBAL sql_mode='SOMETHING'; 更改全局配置永久更改。

          set SESSION sql_mode='SOMETHING'; 更改会话配置 SESSION 变量仅影响当前客户端。

          https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html

          所以我这样做了:

          • 获取 SQL_MODE:SHOW VARIABLES LIKE 'sql_mode';
          • 结果: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
          • 删除结果:NO_ZERO_IN_DATE,NO_ZERO_DATE
          • 设置新配置:set GLOBAL SQL_MODE='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'

          您可以用同样的方法移除或添加其他模式。

          这有助于更改全局以使用和测试框架,或者必须在每个文件或一组查询中指定 sql_mode。

          改编自此处提出的问题:how-can-i-disable-mysql-strict-mode

          示例:安装最新的 Joomla 4.0-alpha 内容。

          编辑: 在PhpMyadmin中,如果你有服务器的控制权,你可以直接在Plus &gt; Variables &gt; sql_mode中更改sql_mode(以及所有其他参数)

          【讨论】:

          • 加 1 谢谢。如果在重新启动 Ubuntu 机器时重置,请将其添加到 /etc/mysql/conf.d
          【解决方案13】:

          这就是我为解决我的问题所做的。我在本地测试 MySQL 5.7 ubuntu 18.04.

          set global sql_mode="NO_ENGINE_SUBSTITUTION";
          

          在全局运行此查询之前,我在 /etc/mysql/conf.d 目录中添加了一个 cnf 文件。 cnf文件名是mysql.cnf和代码

          [mysqld]
          sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ALLOW_INVALID_DATES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
          

          然后我重启mysql

          sudo service mysql restart
          

          希望这可以帮助某人。

          【讨论】:

          • 此解决方案一直有效,直到我重新启动 MySQL 服务器。即使在重新启动后,NO_ENGINE_SUBSTITUTION 的值在SELECT @@global.sql_mode, @@session.sql_mode, @@sql_mode; 的所有列中,但我仍然收到无效日期时间的错误0000-00-00 00:00:00,直到我再次执行第一个查询set global sql_mode="NO_ENGINE_SUBSTITUTION"; 有什么想法吗?
          • 在我的情况下,解决方案是在定义 sql_mode 的 my.ini 行版本中删除 NO_ZERO_IN_DATE,NO_ZERO_DATE
          【解决方案14】:

          检查

          SELECT @@sql_mode;
          

          如果您在其中看到“ZERO_DATE”的内容,请尝试

          SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'NO_ZERO_DATE',''));   
          SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'NO_ZERO_IN_DATE',''));   
          

          注销并重新登录到您的客户端(这很奇怪),然后重试

          【讨论】:

            【解决方案15】:

            如果您手动输入数据,您可以考虑删除 TIMESTAMP(6).000000 上的值和零,使其变为 TIMESTAMP。这对我来说很好。

            【讨论】:

              【解决方案16】:

              这非常难看,但它也为我快速解决了问题。您的表需要一个唯一键,您将使用它来修复受污染的列。在此示例中,主键称为“id”,损坏的时间戳列称为“BadColumn”。

              1. 选择被污染列的 ID。

                select id from table where BadColumn='0000-00-00 00:00:00'

              2. 将 ID 收集到逗号分隔的字符串中。示例:1, 22, 33。我为此使用了一个外部包装器(一个 Perl 脚本)来快速将它们全部吐出。

              3. 使用您的 ID 列表以有效日期(1971 到 2038 年)更新旧列。

                update table set BadColumn='2000-01-01 00:00:00' where id in (1, 22, 33)

              【讨论】:

                【解决方案17】:

                我的解决方案

                SET sql_mode='';
                UPDATE tnx_k2_items
                SET created_by = 790
                , modified = '0000-00-00 00:00:00'
                , modified_by = 0
                

                【讨论】:

                  【解决方案18】:

                  我在https://support.plesk.com/hc/en-us/articles/115000666509-How-to-change-the-SQL-mode-in-MySQL 找到了解决方案。我有这个:

                  mysql> show variables like 'sql_mode';
                  +---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
                  | Variable_name | Value                                                                                                                                     |
                  +---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
                  | sql_mode      | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
                  +---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
                  1 row in set, 1 warning (0.01 sec)
                  

                  请注意上面结果中的NO_ZERO_IN_DATE,NO_ZERO_DATE。我通过这样做删除了它:

                  mysql> SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
                  Query OK, 0 rows affected, 1 warning (0.00 sec)
                  

                  然后我有这个:

                  mysql> show variables like 'sql_mode';
                  +---------------+--------------------------------------------------------------------------------------------------------------+
                  | Variable_name | Value                                                                                                        |
                  +---------------+--------------------------------------------------------------------------------------------------------------+
                  | sql_mode      | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
                  +---------------+--------------------------------------------------------------------------------------------------------------+
                  1 row in set, 1 warning (0.01 sec)
                  

                  这样做之后,我可以成功使用ALTER TABLE 并更改我的表。

                  【讨论】:

                  • 即时:SET sql_mode=(SELECT REPLACE(@@sql_mode, 'NO_ZERO_IN_DATE', ''));和第二个选项一样
                  • 同时添加 ALLOW_INVALID_DATES
                  【解决方案19】:

                  代替

                  UPDATE your_table SET your_column = new_valid_value where your_column = '0000-00-00 00:00:00';
                  

                  使用

                  UPDATE your_table SET your_column = new_valid_value where your_column = 0;
                  

                  【讨论】:

                  • 工作就像一个魅力。在我看来,这个应该被标记为答案。谢谢!
                  • 这拯救了我的一天。谢谢!
                  【解决方案20】:

                  对于 Symfony 用户:为您的 orm 列添加默认值,如下所示:

                  /**
                   * @var DateTime $updatedAt
                   *
                   * @ORM\Column(name="updated_at", type="datetime", nullable=false, options={"default" : "CURRENT_TIMESTAMP"})
                   */
                  

                  【讨论】:

                    【解决方案21】:

                    我认为你应该使用 current_timestamp() 而不是 '0000-00-00 00:00:00'

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2013-07-12
                      • 1970-01-01
                      • 2017-09-21
                      • 2014-02-19
                      • 1970-01-01
                      • 1970-01-01
                      • 2013-06-09
                      相关资源
                      最近更新 更多