MySQL不正确的日期时间值:' 0000-00-00 00:00:00'

时间:2016-02-22 22:23:04

标签: mysql

我最近接手了一个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来处理这个问题。没有考虑太多,我跑了" brew install mysql"其中安装了MySQL 5.7。所以我有一些版本冲突。

我下载了这个数据库的副本并导入了它。

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

  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

我是否必须更新每个值?

19 个答案:

答案 0 :(得分:148)

我无法做到这一点:

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'

答案 1 :(得分:123)

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

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

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

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

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

当您执行" import"时,在该表中执行INSERT的SQL语句在允许零日期的会话中运行。

要查看sql_mode设置:

SHOW VARIABLES LIKE 'sql_mode' ;

-OR -

SELECT @@sql_mode ;

至于如何修复"当前的问题,以便在运行ALTER TABLE语句时不会抛出错误。

有几个选择:

1)删除sql_modeNO_ZERO_DATE,将NO_ZERO_IN_DATE更改为允许零日期。更改可以在my.cnf文件中应用,因此重启MySQL Server后,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列以允许NULL值,并更新现有行以将零日期更改为空值

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' a"零日期"。这将被评估为{{1 }}

答案 2 :(得分:36)

根据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

答案 3 :(得分:10)

我的建议是,表是空的还是非常大的情况是将create语句导出为.sql文件,根据需要重写它们。如果您有任何现有数据,也可以执行相同的操作,即导出插入语句(我建议在单独的文件中将其作为create语句执行)。最后,删除表并执行第一个create语句,然后插入。

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

答案 4 :(得分:9)

我在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并更改我的表。

答案 5 :(得分:7)

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'

答案 6 :(得分:7)

代替

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;

答案 7 :(得分:5)

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

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

答案 8 :(得分:3)

使sql模式变得不严格

如果使用laravel转到config-> database,请转到mysql设置并将严格模式设置为假

答案 9 :(得分:2)

我也有

  

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:00 unix时间戳为0

答案 10 :(得分:2)

我的解决方案

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

答案 11 :(得分:1)

在将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确定

...

工作正常:)

答案 12 :(得分:1)

这是我的解决方案 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内容。

答案 13 :(得分:1)

检查

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',''));   

退出并再次登录到您的客户端(这很奇怪),然后重试

答案 14 :(得分:1)

这非常丑陋,但这也为我迅速解决了这个问题。您的表需要一个唯一的键,该键将用于修复污染的列。在此示例中,主键称为“ 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)

答案 15 :(得分:0)

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

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

答案 16 :(得分:0)

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

答案 17 :(得分:0)

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

error: expected primary-expression before ‘]’ token
    matrix_t * matrix = new matrix_t[matrix_t];
                                             ^

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

set global sql_mode="NO_ENGINE_SUBSTITUTION";

然后我重新启动mysql

[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

希望这可以帮助某人。

答案 18 :(得分:0)

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

所以首先我更新表:

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

至少在我看来,问题解决了。