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

我必须更新每个值吗?


当前回答

我不能这样做:

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

(MySQL 5.7.13)。

我一直得到错误的日期时间值:'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'

其他回答

使sql模式不严格

如果使用laravel,进入config->数据库,进入mysql设置,使严格模式为假

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

选择受污染列的id。 select id from BadColumn='0000-00-00 00:00:00' 将id收集到一个以逗号分隔的字符串中。例如:1,22,33。为此,我使用了一个外部包装器(Perl脚本)来快速地将它们全部输出。 使用id列表使用有效日期(1971年到2038年)更新旧列。 更新表BadColumn='2000-01-01 00:00:00' where id in (1,22,33)

我的建议是,如果表是空的或者不是很大,那就把create语句导出为一个.sql文件,然后按照你的意愿重写它们。如果你有任何现有的数据,也做同样的事情,即导出插入语句(我建议在一个单独的文件中作为创建语句)。最后,删除表并执行第一个create语句,然后执行insert语句。

你可以使用mysqldump命令,包括在你的MySQL安装中,或者你也可以安装MySQL工作台,这是一个免费的图形工具,它也包括这个选项,以一种非常自定义的方式,而不必寻找特定的命令选项。

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

如果手动输入数据,可以考虑删除时间戳(6)上的值和零。000000,这样它就变成了TIMESTAMP。这对我来说很有效。