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


155

我最近接手了一个十年前创建的旧项目。它使用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

我需要更新每个值吗?

Answers:


12

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

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


露西亚·帕萨林(Lucia Pasarin),我非常喜欢您的想法,但是数据会不会被截断?是否某些UTF8数据比latin1占用更多的字节?如果以前在varchar 255中适合某些东西,也许现在不可以了吗?我是否应该将所有varchars更改为“文本”字段?
洛姆'16

是的,你是对的。之所以会发生这种情况,是因为latin1每个字符使用1个字节,而utf8每个字符最多使用4个字节(取决于MySQL的版本和utf8的类型。dev.mysql.com/ doc/refman/5.5/en/charset-unicode -utf8.html)。因此,您不一定需要TEXT类型。我认为x4您以前存在的大小应该工作。
Lucia Pasarin '16

这与要删除文件时重新格式化硬盘驱动器的数据库等效。可以肯定地说此解决方案在生产环境中是不可接受的。
布兰登'18

198

我无法执行此操作:

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'

13
我也遇到了同样的问题,但是将最后一部分设置为0,对我来说是这样的:UPDATE users SET created = NULL WHERE created = '0'
Brian Leishman

SELECT * FROM entityWHERE createdAt =“ 0000-00-00 00:00:00”工作正常,但是更新失败!我有同样的问题。使用@obe CAST(创建为AS CHAR(20))的解决方案对其进行修复...我认为这是一个错误。
Chrysweel

44
对我来说,它的工作方式就像UPDATE users SET created = NULL WHERE created=0(没有'大约为零)
KIR

1
为了只替换没有时间戳的“ 0000-00-00”日期,我使用了CHAR(11)
D.Tate

1
那真是天才。为什么这不是公认的答案?仅对于没有时间戳记的日期,最小值为1000-01-01。考虑将其用作每个打算保留为空或具有0000-00-00值的日期属性的默认值。
Arvanitis Christos

155

使用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)sql_mode删除NO_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值,并更新现有行以将零日期更改为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' “零日期”。'0000-00-00 00:00:00'


3
是的,这对我有用。我在my.ini文件中搜索sql-mode,并删除了NO_ZERO_IN_DATE和NO_ZERO_DATE。然后重新启动服务。谢谢spencer7593!
米里


当我执行#2“将创建的列更改为允许NULL值”时,它不会让我这样做,因为列值仍然会产生错误。完全卡住了。
迈克·威尔

1
@MikeWeir:大概“ 不会让我 ”意味着执行SQL语句时返回错误。可能是由于的设置sql_mode。MySQL的最新版本的默认设置sql_mode比以前的版本更严格。参考8.0位置:dev.mysql.com/doc/refman/8.0/en/sql-mode.htmlNO_ZERO_DATEALLOW_INVALID_DATE等。注意,一些包含在严格模式例如STRICT_TRANS_TABLESSTRICT_ALL_TABLES和其他的组合模式。要变通解决这些限制,请临时修改会话的
sql_mode

肯定是@ spencer7593。我也不觉得那个选择是最好的。我听取了您建议设置一个非常旧的日期值(1970)的建议,而我的系统只会忽略它。感谢您提供的所有详细信息。
迈克·威尔

67

我通过在查询之前执行此操作来解决它

SET SQL_MODE='ALLOW_INVALID_DATES';

这是唯一的答案。用作:--init-command ='SET SESSION FOREIGN_KEY_CHECKS = 0; SET SQL_MODE ='ALLOW_INVALID_DATES'–
Konchog

非常感谢。无法解释这个问题给我带来的痛苦。
Dieter Gribnitz

42

根据MySQL 5.7参考手册

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

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'

4
讨论如何解决此问题将对此答案有所改善。
KevinO

这会影响日期时间存储吗?或引起任何问题
字节

8

这是我的解决方案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/zh-CN/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。

从这里提出的问题改编而成:如何禁用MySQL严格模式

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

编辑: 在PhpMyadmin中,如果您拥有服务器的控制权,则可以sql_mode直接在(和所有其他参数)中更改Plus > Variables > sql_mode


5

您可以更改的类型创建字段从datetimevarchar(255),那么你可以设置(更新)有值的所有记录"0000-00-00 00:00:00"NULL

现在,您可以进行查询而不会出现错误。完成后,您可以将创建的字段的类型更改为datetime


4

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

...

效果很好:)


4

检查一下

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

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


3

使SQL模式不严格

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


我可以在myphpadmin中这样做吗?
唐·金

phpMyAdmin只是使用实际mysql服务器的接口,无论您使用哪个mysql命令接口都不会随该接口更改。尝试使用此命令(设置sql_mode ='';;)或此命令(设置全局sql_mode ='';;)将其关闭。
Milind Chaudhary

1
是的,我发现关闭严格模式所需的所有内容都是通过my.cnf的底部添加sql_mode =(此后什么也没有)
Don King

3

我有一个类似的问题,但在我的情况下,某些行的值为NULL。

所以首先我更新表:

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

问题解决了,至少就我而言。


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


1
问题是OP由于错误而无法更改日期。我猜是NO_ZERO_DATE
GusDeCooL

2

我在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成功使用并更改表。


1

这就是我解决问题的方法。我在本地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";
Smamatti

在我的情况下,解决方案是NO_ZERO_IN_DATE,NO_ZERO_DATE在您的my.ini行版本中删除sql_mode
Smamatti

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)


1

我的解决方案

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

1

代替

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;

0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.