如何在整个数据库中更改 CHARACTER SET(和 COLLATION)?

Posted

技术标签:

【中文标题】如何在整个数据库中更改 CHARACTER SET(和 COLLATION)?【英文标题】:How to change the CHARACTER SET (and COLLATION) throughout a database? 【发布时间】:2011-08-19 21:06:31 【问题描述】:

我们以前的程序员在表 (mysql) 中设置了错误的排序规则。他用拉丁排序规则设置它,它应该是 UTF8,现在我遇到了问题。每条带有中日字符的记录都变成了???字符。

是否可以更改排序规则并取回字符的详细信息?

【问题讨论】:

MySql alter table Collation 的可能重复项 排序规则与'???'有什么关系字符集?我认为这与字符集有关? 我正在更改标题以反映意图。更改 databasedefault 排序规则远低于预期。 【参考方案1】:

更改数据库排序规则:

ALTER DATABASE <database_name> CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;

更改表排序规则:

ALTER TABLE <table_name> CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;

更改列排序规则:

ALTER TABLE <table_name> MODIFY <column_name> VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;

utf8mb4_0900_ai_ci 各部分是什么意思?

3 bytes -- utf8
4 bytes -- utf8mb4 (new)
v4.0 --   _unicode_
v5.20 --  _unicode_520_
v9.0 --   _0900_ (new)
_bin      -- just compare the bits; don't consider case folding, accents, etc
_ci       -- explicitly case insensitive (A=a) and implicitly accent insensitive (a=á)
_ai_ci    -- explicitly case insensitive and accent insensitive
_as (etc) -- accent-sensitive (etc)
_bin         -- simple, fast
_general_ci  -- fails to compare multiletters; eg ss=ß, somewhat fast
...          -- slower
_0900_       -- (8.0) much faster because of a rewrite

更多信息:

What are the differences between utf8_general_ci and utf8_unicode_ci? What's the difference between utf8_general_ci and utf8_unicode_ci? How to change collation of database, table, column? What's the difference between utf8_general_ci and utf8_unicode_ci?

【讨论】:

当心CHARACTER SET utf8 将默认为utf8_general_ci,但如果需要,您也可以像ALTER DATABASE &lt;database_name&gt; CHARACTER SET utf8 COLLATE utf8_unicode_ci; 这样定义排序规则 ...我建议你测试一下create table testit(a varchar(1)); show create table testit \G drop table testit; 只想提一下,第二个会把排序规则改成utf8_general_ci;如果你想把它改成utf8_unicode_ci,你可以定义排序规则:ALTER TABLE &lt;table_name&gt; CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;。正如@KCD 所指出的,这在表上的工作方式与在数据库上的工作方式完全相同。 最好执行以下操作以获得完整的 utf8 支持 ALTER DATABASE &lt;database_name&gt; CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci。您应该对其他两个语句执行相同的操作。 你真的需要使用“ALTER TABLE MODIFY ...”。根据dev.mysql.com/doc/refman/5.5/en/alter-table.html,似乎“ALTER TABLE CONVERT TO CHARACTER SET ...”也改变了列?或者我没有正确阅读/理解手册。【参考方案2】:

除了 David Whittaker 发布的内容之外,我还创建了一个查询,该查询生成完整的表和列更改语句,该语句将转换每个表。运行可能是个好主意

设置会话 group_concat_max_len = 100000;

首先要确保您的组 concat 不会超过 here 所看到的非常小的限制。

     SELECT a.table_name, concat('ALTER TABLE ', a.table_schema, '.', a.table_name, ' DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci, ',
        group_concat(distinct(concat(' MODIFY ',  column_name, ' ', column_type, ' CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ', if (is_nullable = 'NO', ' NOT', ''), ' NULL ',
        if (COLUMN_DEFAULT is not null, CONCAT(' DEFAULT \'', COLUMN_DEFAULT, '\''), ''), if (EXTRA != '', CONCAT(' ', EXTRA), '')))), ';') as alter_statement
    FROM information_schema.columns a
    INNER JOIN INFORMATION_SCHEMA.TABLES b ON a.TABLE_CATALOG = b.TABLE_CATALOG
        AND a.TABLE_SCHEMA = b.TABLE_SCHEMA
        AND a.TABLE_NAME = b.TABLE_NAME
        AND b.table_type != 'view'
    WHERE a.table_schema = ? and (collation_name = 'latin1_swedish_ci' or collation_name = 'utf8mb4_general_ci')
    GROUP BY table_name;

上一个答案之间的区别在于它使用 utf8 而不是 ut8mb4 并且使用 t1.data_type 和 t1.CHARACTER_MAXIMUM_LENGTH 不适用于枚举。此外,我的查询不包括视图,因为它们必须单独更改。

我只是使用 Perl 脚本将所有这些更改作为数组返回并对其进行迭代,修复了太长的列(通常它们是 varchar(256),而数据中通常只有 20 个字符,所以这是一个简单的解决方法)。

从 latin1 -> utf8mb4 更改时,我发现一些数据已损坏。似乎列中的 utf8 编码 latin1 字符会在转换中出错。我只是在更改前后保存了我知道会在内存中出现问题的列中的数据,然后比较它们并生成更新语句来修复数据。

【讨论】:

【参考方案3】:

以下是如何更改所有数据库/表/列。运行这些查询,它们将输出将整个架构转换为 utf8 所需的所有后续查询。希望这会有所帮助!

-- 更改数据库默认排序规则

SELECT DISTINCT concat('ALTER DATABASE `', TABLE_SCHEMA, '` CHARACTER SET utf8 COLLATE utf8_unicode_ci;')
from information_schema.tables
where TABLE_SCHEMA like  'database_name';

-- 更改 TABLE 排序规则/字符集

SELECT concat('ALTER TABLE `', TABLE_SCHEMA, '`.`', table_name, '` CHARACTER SET utf8 COLLATE utf8_unicode_ci;')
from information_schema.tables
where TABLE_SCHEMA like 'database_name';

-- 更改 COLUMN 排序规则/字符集

SELECT concat('ALTER TABLE `', t1.TABLE_SCHEMA, '`.`', t1.table_name, '` MODIFY `', t1.column_name, '` ', t1.data_type , '(' , t1.CHARACTER_MAXIMUM_LENGTH , ')' , ' CHARACTER SET utf8 COLLATE utf8_unicode_ci;')
from information_schema.columns t1
where t1.TABLE_SCHEMA like 'database_name' and t1.COLLATION_NAME = 'old_charset_name';

【讨论】:

好。 !我试图解决同样的问题大约需要一个小时。我使用这 3 个命令,我看到字符集发生了变化。但主要问题仍然是我。如果我直接写入数据库,那么一切都在我的浏览器中显示得很好。但是如果我从网站表单中添加了一些内容,数据库中的结果只是??????。有什么我应该考虑的吗?我的网络应用是一个 .NET MVC 应用。 保存到对未来项目有用的查询中。 我提出了一些修改建议,因为这些自动查询的使用还不是很安全。 CHARACTER_MAXIMUM_LENGTH 仍然存在问题:当您更改时,原来的可能太高了,例如latin1_swedish_ci 到 utf8_unicode_ci。 这是一个很好的答案。我有三个 cmets/问题: 1) 为什么在 COLUMN 代码中使用“t1”?我认为没有任何必要。 2) 为什么是“t1.data_type, '(', t1.CHARACTER_MAXIMUM_LENGTH, ')'”而不仅仅是“t1.column_type”? 3)为什么大写和小写的混合 - TABLE_SCHEMA vs table_name 等等? 请注意,这会将列更改为 NULLable 并删除其默认值(至少在 MySQL 中)。【参考方案4】:

请注意,在 Mysql 中,utf8 字符集只是真正的 UTF8 字符集的一个子集。为了节省一个字节的存储空间,Mysql 团队决定只存储一个 UTF8 字符的三个字节,而不是完整的四个字节。这意味着不完全支持某些东亚语言和表情符号。为确保您可以存储所有 UTF8 字符,请在 Mysql 中使用 utf8mb4 数据类型和 utf8mb4_binutf8mb4_general_ci

【讨论】:

到目前为止,建议使用utf8mb4_unicode_ci 而不是utf8mb4_general_ci。见***.com/questions/766809/… 和drupal.stackexchange.com/questions/166405/…【参考方案5】:

here 很好地描述了这个过程。然而,一些不适合拉丁空间的角色永远消失了。 UTF-8 是 latin1 的 SUPERSET。不是反过来。大多数将适合单字节空间,但任何未定义的都不会(检查 latin1 列表 - 并非所有 256 个字符都已定义,具体取决于 mysql 的 latin1 定义)

【讨论】:

以上是关于如何在整个数据库中更改 CHARACTER SET(和 COLLATION)?的主要内容,如果未能解决你的问题,请参考以下文章

如何在整个数据库中更改 CHARACTER SET(和 COLLATION)?

如何更改 Amazon Aurora DB 中的 character_set_server?

更改 MySQL db 的 character_set 和 collat​​ion 时是不是会影响现有索引

mysql设置字符集CHARACTER SET

为啥要修改数据库字符集

mysql 更改字符集