如何清理 MySQL 中带有特殊字符的数据
How to clean data with special characters in MySQL
一个干净的数据怎么能像这样 Réation
、l’Oreal
分别在 MySQL 中看起来像这样 R'action
和 L'Oreal
?
这看起来像是 "double encoding" 的一个例子。这是右手在说 utf8,而左手在听 latin1 的地方。参见 and See also http://mysql.rjweb.org/doc.php/charcoll#fixes_for_various_cases。
Réation
-> Réation
取消双重编码后
但你说 R'action
-- 我想知道你输入的 é
是 e'
还是 'e
?
我还假设你的意思是 L’Oreal
? (注意 'right single quote mark' 而不是 'apostrophe'。)
首先,我们需要验证它是否真的是一个普通的双重编码。
SELECT col, HEX(col) FROM ... WHERE ...
应该给你这个 Réation
的十六进制:
52 E9 6174696F6E -- latin1 encoding
52 C3A9 6174696F6E -- utf8 encoding
52 C383 C2A9 6174696F6E -- double encoding
(忽略间距。)
如果你得到了其中的第三个,请继续我的答案。 如果你得到任何其他东西,停止! -- 问题比我想象的要复杂
现在,看看双重编码修复是否会修复它(修复之前):
SELECT col, CONVERT(BINARY(CONVERT(CONVERT(
BINARY(CONVERT(col USING latin1)) USING utf8mb4)
USING latin1)) USING utf8mb4)
FROM tbl;
你需要防止它发生和修复数据。 以下部分是不可逆的;在 table!
的副本上测试它
您的案例是:CHARACTER SET latin1
,但其中有 utf8/utf8mb4 个字节;修复字符集时保留字节:
首先,假设您有 tbl.col 的声明:
col VARCHAR(111) CHARACTER SET latin1 NOT NULL
然后在不更改字节的情况下转换列:
ALTER TABLE tbl MODIFY COLUMN col VARBINARY(111) NOT NULL;
ALTER TABLE tbl MODIFY COLUMN col VARCHAR(111) CHARACTER SET utf8mb4 NOT NULL;
注意:如果您以 TEXT
开头,请使用 BLOB
作为中间定义。 (一定要保持其他规格相同 - VARCHAR
、NOT NULL
等)
对每个 table 中有问题的每一列都这样做。
(在本次讨论中,我不区分 utf8mb4 和 utf8。大多数文本都非常满意;Emoji 和一些中文需要 utf8mb4,而不仅仅是 utf8。)
来自评论
CONVERT(UNHEX('C38EC2B2') USING utf8mb4) = 'β' (Greek beta)
CONVERT(CONVERT(UNHEX('C38EC2B2') USING latin1) USING utf8mb4) = 'β'
我的结论:首先你有一些配置错误。然后您应用了一项或多项 错误的 修复。你现在乱七八糟,我不敢帮你解开。也就是说,混乱不仅仅是 "double encoding".
如果可能,重新开始,确保在添加更多数据之前正确存储了一些测试数据。如果数据不好,不要尝试修复数据;退后并重新开始。请参阅 "Trouble..." 中的 "best bractice" 以正确设置。我会在附近帮助您解释您在 table 中看到的十六进制是否正确。
一个干净的数据怎么能像这样 Réation
、l’Oreal
分别在 MySQL 中看起来像这样 R'action
和 L'Oreal
?
这看起来像是 "double encoding" 的一个例子。这是右手在说 utf8,而左手在听 latin1 的地方。参见
Réation
-> Réation
取消双重编码后
但你说 R'action
-- 我想知道你输入的 é
是 e'
还是 'e
?
我还假设你的意思是 L’Oreal
? (注意 'right single quote mark' 而不是 'apostrophe'。)
首先,我们需要验证它是否真的是一个普通的双重编码。
SELECT col, HEX(col) FROM ... WHERE ...
应该给你这个 Réation
的十六进制:
52 E9 6174696F6E -- latin1 encoding
52 C3A9 6174696F6E -- utf8 encoding
52 C383 C2A9 6174696F6E -- double encoding
(忽略间距。)
如果你得到了其中的第三个,请继续我的答案。 如果你得到任何其他东西,停止! -- 问题比我想象的要复杂
现在,看看双重编码修复是否会修复它(修复之前):
SELECT col, CONVERT(BINARY(CONVERT(CONVERT(
BINARY(CONVERT(col USING latin1)) USING utf8mb4)
USING latin1)) USING utf8mb4)
FROM tbl;
你需要防止它发生和修复数据。 以下部分是不可逆的;在 table!
的副本上测试它您的案例是:CHARACTER SET latin1
,但其中有 utf8/utf8mb4 个字节;修复字符集时保留字节:
首先,假设您有 tbl.col 的声明:
col VARCHAR(111) CHARACTER SET latin1 NOT NULL
然后在不更改字节的情况下转换列:
ALTER TABLE tbl MODIFY COLUMN col VARBINARY(111) NOT NULL;
ALTER TABLE tbl MODIFY COLUMN col VARCHAR(111) CHARACTER SET utf8mb4 NOT NULL;
注意:如果您以 TEXT
开头,请使用 BLOB
作为中间定义。 (一定要保持其他规格相同 - VARCHAR
、NOT NULL
等)
对每个 table 中有问题的每一列都这样做。
(在本次讨论中,我不区分 utf8mb4 和 utf8。大多数文本都非常满意;Emoji 和一些中文需要 utf8mb4,而不仅仅是 utf8。)
来自评论
CONVERT(UNHEX('C38EC2B2') USING utf8mb4) = 'β' (Greek beta)
CONVERT(CONVERT(UNHEX('C38EC2B2') USING latin1) USING utf8mb4) = 'β'
我的结论:首先你有一些配置错误。然后您应用了一项或多项 错误的 修复。你现在乱七八糟,我不敢帮你解开。也就是说,混乱不仅仅是 "double encoding".
如果可能,重新开始,确保在添加更多数据之前正确存储了一些测试数据。如果数据不好,不要尝试修复数据;退后并重新开始。请参阅 "Trouble..." 中的 "best bractice" 以正确设置。我会在附近帮助您解释您在 table 中看到的十六进制是否正确。