我最近接手了一个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

我必须更新每个值吗?


当前回答

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

所以首先我更新表:

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

问题解决了,至少对我来说。

其他回答

使用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' ;

-or-

SELECT @@sql_mode ;

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

几个选项:

1)通过删除NO_ZERO_DATE和NO_ZERO_IN_DATE,将sql_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)将创建的列更改为允许空值,并更新现有行以将零日期更改为空值

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


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

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

如果我们已经修改了创建的列以允许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'

(注意,datetime值为1970年1月1日午夜('1970-01-01 00:00:00')是一个“零日期”。它将被计算为'0000-00-00 00:00:00'

我的解决方案

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

检查

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

登出并再次返回到您的客户机(这很奇怪),然后再试一次

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'

参考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'

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

ALTER TABLE users
ALTER created SET DEFAULT CURRENT_TIMESTAMP