MySQL:将列更改为UTF8时数据被损坏

时间:2019-12-19 17:29:08

标签: mysql utf-8

我正在将在LATIN1中创建的MySQL数据库迁移到UTF8。为此,我首先将每一列更改为相应的二进制类型,然后更改为UTF8:

ALTER TABLE clientes CHARACTER SET utf8;
ALTER TABLE clientes change nombre nombre varbinary(255);
ALTER TABLE clientes change nombre nombre varchar(255) character set utf8;

因为,根据所有文档,这是防止数据损坏的正确方法...

...但是,数据仍然被破坏。我只举两个例子:

  • 单词“Larrasoaña”在“ñ”处被截断,错误:警告:#1366不正确的字符串值:'nomx'列的'\ xF1a'
  • “JesúsyMaría”一词在“ú”处被截断,错误为警告:#1366不正确的字符串值:“ nombre”列为“ \ xFAs y M ...”

这些数据是如何输入的?嗯,数据库是PHP Web应用程序的后端,该数据库使用UTF8进行所有操作(包括使用“ SET NAMES UTF8” 连接到MySQL服务器)...除了正确创建数据库外。因此,我假设添加的所有数据都在UTF8中。

总结:看来我在LATIN1列中存储了UTF8文本,现在我尝试将列更改为UTF8,文本被截断了。

为什么会这样?我该怎么办?

编辑:忘记了,由于我没有命令行访问权限,因此我正在通过PhpMyAdmin执行所有操作。

1 个答案:

答案 0 :(得分:1)

F1和FA是latin1编码。您需要告诉MySQL数据为latin1。一种方法是通过SET NAMES latin1

但是请注意...这与要存储数据的列的设置无关。而且,如今,utf8mb4是文本的首选设置。 MySQL将在列的编码和客户端的编码之间转换。但是您必须通过连接参数(或SET NAMES)告诉它客户端的编码。

这对ALTER TABLEs 适用于某些情况,而不是所有情况!您可能想要http://mysql.rjweb.org/doc.php/charcoll#fixes_for_various_cases

中的第一个条目
  

表是字符集latin1,并在latin1中正确编码;想   utf8mb4:

ALTER TABLE tbl CONVERT TO CHARACTER SET utf8mb4;

我不知道您的数据是否无法挽回。请提供其中一行以及十六进制。

十六进制

  

“Larrasoaña”编码为4C61727261736F61 F1 61,“JesúsyMaría”编码为4A6573 FA 732079204D6172 ED 6120

那些是latin1编码的(或latin5或dec8)。如果表定义(SHOW CREATE TABLE)为latin1,则您可以保留它。 (latin1处理西欧语言,但不处理亚洲语言。)

如果要将所有文本列都转换为utf8或utf8mb4,请像我上面介绍的那样执行ALTER。您的3-Alter方法将不能正常运行;假设latin1列中的字节实际上是UTF-8字节(不是)。

但是...您必须根据客户端的需求指定客户端的编码。客户端和表是否一致并不重要,因为将提供转换。

为什么三步更改失败

ALTER TABLE clientes CHARACTER SET utf8;-设置 new 列的默认字符集。它不会影响现有的列定义以及这些列中的任何数据。

ALTER TABLE clientes change nombre nombre varbinary(255); -表示“忘记任何文本编码”。那就是F1现在只是一堆比特,而不是ñ的latin1表示形式。

ALTER TABLE clientes change nombre nombre varchar(255) character set utf8;-这将使用这些 varbinary 位,并说“让我们将它们视为utf8。这会产生错误消息,因为F1不是utf8的有效编码

如果字节已经是 utf8字节,则该过程是适当的。也就是说,如果它已经是C3B1的2字节ñ。 (顺便说一句,这通常表现为“ Mojibake”,当解释为latin1时显示为ñ。)

1-Alter程序...

ALTER TABLE clientes CONVERT TO CHARACTER SET utf8;(用于转换整个表)或ALTER TABLE clientes MODIFY nombre varchar(255) character set utf8;(仅用于转换一列)。他们执行以下操作:

对于每个文本(char / varchar / text)列,它将根据其当前编码(latin1,F1)读取数据,将其转换为utf8(或utf8mb4)(C3B1),然后写回该行。同时,它已将声明更改为CHARACTER SET utf8

也就是说,这是更改CHARACTER SET而不更改“文本”的“正确”过程。没错,编码已更改(F1-> C3B1),但这与对CHARACTER SET的更改保持一致。

恢复

您的前2个ALTER有效,对吗?第三个人成功了,失败了,还是丢了桌子?

如果中止,将varbinary保留在原位,然后再进行2次修改:首先回到latin1;然后直接转到utf8。

如果它给您留下了乱七八糟的列,特别是如果行被截断了,那么您需要返回到备份,或者以其他方式重新加载数据。