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

lor*_*orm 125 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
Run Code Online (Sandbox Code Playgroud)

我设置了自己的Mac来处理这个问题.在没有考虑太多的情况下,我运行了安装MySQL 5.7的"brew install mysql".所以我有一些版本冲突.

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

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

  ALTER TABLE users MODIFY first_name varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci    NOT NULL  
Run Code Online (Sandbox Code Playgroud)

我收到此错误:

  ERROR 1292 (22007): Incorrect datetime value: '0000-00-00 00:00:00' for column 'created' at row 1
Run Code Online (Sandbox Code Playgroud)

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

  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
Run Code Online (Sandbox Code Playgroud)

但我得到:

  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
Run Code Online (Sandbox Code Playgroud)

我是否必须更新每个值?

obe*_*obe 174

我无法做到这一点:

UPDATE users SET created = NULL WHERE created = '0000-00-00 00:00:00'
Run Code Online (Sandbox Code Playgroud)

(在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'
Run Code Online (Sandbox Code Playgroud)

  • 对我来说,它就像`UPDATE用户SET created = NULL WHERE created = 0`(没有'零左右) (29认同)
  • 我有同样的问题,但将最后一部分设置为0就像我这样修复了它:`UPDATE users SET created = NULL WHERE created ='0' (8认同)
  • 为了仅替换没有时间戳的“0000-00-00”日期,我使用了 CHAR(11) (2认同)
  • 那是纯粹的天才。为什么这不是公认的答案?对于没有时间戳的日期,最小值为 1000-01-01。考虑将其用作您打算留空或使用 0000-00-00 值的每个日期属性的默认值。 (2认同)

spe*_*593 139

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

 ALTER TABLE users MODIFY created datetime  NULL DEFAULT '1970-01-02'
Run Code Online (Sandbox Code Playgroud)

...不会更改已存储的任何值."default"值适用于插入的行,并且不为该列提供值.


至于您遇到错误的原因,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' ;
Run Code Online (Sandbox Code Playgroud)

-要么-

SELECT @@sql_mode ;
Run Code Online (Sandbox Code Playgroud)

至于如何"修复"当前问题,以便在运行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 ;
Run Code Online (Sandbox Code Playgroud)

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

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


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

在这个问题中,AUTO_INCREMENT表定义显示的值确保我们行数不会过多.

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

UPDATE  `users` SET `created` = NULL WHERE `created` = '0000-00-00 00:00:00'
Run Code Online (Sandbox Code Playgroud)

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

UPDATE  `users` SET `created` = '1970-01-02' WHERE `created` = '0000-00-00 00:00:00'
Run Code Online (Sandbox Code Playgroud)

(请注意,午夜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.然后重新启动该服务.谢谢spencer7593! (3认同)

Tar*_*han 43

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

SET SQL_MODE ='ALLOW_INVALID_DATES';

  • 是的!救命的答案。我这样使用它: `mysql -u username -p database --init-command="SET SESSION FOREIGN_KEY_CHECKS=0;SET SQL_MODE='ALLOW_INVALID_DATES';" < 文件名.sql` (3认同)
  • MySQL 8.0 中需要此功能,同时添加此功能并在“/etc/mysql/conf.d/”内相应文件中从 sql 模式中删除“NO_ZERO_IN_DATE”和“NO_ZERO_DATE” (2认同)

Iva*_*lho 39

根据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'
Run Code Online (Sandbox Code Playgroud)

另外,为了避免这个问题,我建议你总是将当前时间设置为类似created字段的默认值,这样它们就会自动填充INSERT.做就是了:

ALTER TABLE users
ALTER created SET DEFAULT CURRENT_TIMESTAMP
Run Code Online (Sandbox Code Playgroud)


小智 19

代替

UPDATE your_table SET your_column = new_valid_value where your_column = '0000-00-00 00:00:00';
Run Code Online (Sandbox Code Playgroud)

UPDATE your_table SET your_column = new_valid_value where your_column = 0;
Run Code Online (Sandbox Code Playgroud)

  • 工作起来就像一个魅力。我认为这个应该被标记为答案。谢谢! (2认同)

Luc*_*rin 11

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

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

  • 这相当于当您想要删除文件时重新格式化硬盘驱动器的数据库。可以肯定地说,这个解决方案在生产环境中是不可接受的。 (2认同)

Jai*_*oya 11

我在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)
Run Code Online (Sandbox Code Playgroud)

注意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)
Run Code Online (Sandbox Code Playgroud)

然后我有这个:

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)
Run Code Online (Sandbox Code Playgroud)

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


Nik*_*kar 10

发生这个问题是因为 MYSQL 5.7 引入了默认模式,例如

SELECT @@GLOBAL.sql_mode global, @@SESSION.sql_mode session;
=> ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
Run Code Online (Sandbox Code Playgroud)

在默认模式中,有一种模式NO_ZERO_DATE会阻止您将值放入 0000-00-00 00:00:00日期时间列中。

临时修复方法是从默认模式中删除模式NO_ZERO_DATE/NO_ZERO_IN_DATE并保持其他模式不变。

SET GLOBAL sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
Run Code Online (Sandbox Code Playgroud)

但是,一旦您重新启动 mysql 服务器,所有临时设置都将消失,默认模式将再次启用。要在重新启动时修复此问题,请更改计算机上 my.cnf 文件中的默认模式。

请参阅文档:- https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#:~:text=To%20set%20the%20SQL%20mode,ini%20(Windows)

我个人的观点:-正确的开发实践是在列中放置正确的日期时间值而不是0000-00-00 00:00:00. 这样您就不必担心 SQL 模式。


com*_*ike 8

查看

SELECT @@sql_mode;
Run Code Online (Sandbox Code Playgroud)

如果你在那里看到“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',''));   
Run Code Online (Sandbox Code Playgroud)

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


小智 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'
Run Code Online (Sandbox Code Playgroud)

  • 通过讨论解决问题的原因,可以改进这个答案. (3认同)

Kal*_*aaz 6

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

set global sql_mode="NO_ENGINE_SUBSTITUTION";
Run Code Online (Sandbox Code Playgroud)

在全局运行此查询之前,我在 /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
Run Code Online (Sandbox Code Playgroud)

然后我重新启动mysql

sudo service mysql restart
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助别人。


小智 5

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

现在,您可以毫无错误地进行查询.你完成后,你可以改变字段的类型创建datetime.


Shi*_*Sao 5

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