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


172

我们以前的程序员在表(Mysql)中设置了错误的排序规则。他使用拉丁文归类(应为UTF8)进行设置,现在我遇到了问题。每个带有中文和日文字符的记录都变成??? 字符。

是否可以更改排序规则并找回角色的细节?



排序规则与“ ???”有什么关系 字符集?我认为这与字符集有关?
peterchaula

我正在更改标题以反映其意图。更改数据库默认排序规则比期望的要少得多。
瑞克·詹姆斯

Answers:


365

更改数据库排序规则:

ALTER DATABASE <database_name> CHARACTER SET utf8 COLLATE utf8mb4_0900_ai_ci;

更改表排序规则:

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

更改列排序规则:

ALTER TABLE <table_name> MODIFY <column_name> VARCHAR(255) CHARACTER SET utf8 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 multiple letters; eg ss=ß, somewhat fast
...          -- slower
_0900_       -- (8.0) much faster because of a rewrite

更多信息:


4
当心CHARACTER SET utf8将默认为默认值,utf8_general_ci但您也可以ALTER DATABASE <database_name> CHARACTER SET utf8 COLLATE utf8_unicode_ci;根据需要定义排序规则
KCD 2014年

1
...并且我建议您对其进行测试create table testit(a varchar(1)); show create table testit \G drop table testit;
KCD 2014年

2
只想提一下第二个将排序规则更改为utf8_general_ci; 如果要将其更改为utf8_unicode_ci,则可以定义归类: ALTER TABLE <table_name> CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;。正如@KCD所指出的,这在表上的工作与在数据库上的工作完全相同。
wiser 2015年

9
为了获得完整的utf8支持,最好执行以下操作ALTER DATABASE <database_name> CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci。您应该对其他两个语句执行相同的操作。
2015年

您是否真的需要使用“ ALTER TABLE <table_name> MODIFY <column_name> ...”。根据dev.mysql.com/doc/refman/5.5/en/alter-table.html的说法,“ ALTER TABLE <table_name>转换为字符集...”似乎也更改了列?也许我没有正确阅读/理解本手册。
hansfn

49

以下是如何更改所有数据库/表/列的方法。运行这些查询,它们将输出将整个模式转换为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';

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

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

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

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条命令,我发现字符集已更改。但是主要的问题仍然存在。如果我直接写数据库,那么一切都将在浏览器中很好地显示。但是,如果我从网站表单中添加了一些内容,则数据库中的结果仅为??????。有什么我应该考虑的吗?我的Web应用程序是.NET MVC应用程序。
Tchaps

保存到对将来的项目有用的查询中。
Manatax'3

我建议您进行一些修改,因为这些自动化查询还不太安全。CHARACTER_MAXIMUM_LENGTH仍然存在一个问题:当您从例如latin1_swedish_ci更改为utf8_unicode_ci时,原始的可能会太高。
鲁宾

1
这是一个很好的答案。我有三个评论/问题:1)为什么在COLUMN代码中使用“ t1”?我没有任何需要。2)为什么是“ t1.data_type,'(',t1.CHARACTER_MAXIMUM_LENGTH,')'”而不仅仅是“ t1.column_type”?3)为什么大写和小写混合-TABLE_SCHEMA vs table_name等?
hansfn

25

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


1
现在,建议使用utf8mb4_unicode_ci代替utf8mb4_general_ci。见stackoverflow.com/questions/766809/...drupal.stackexchange.com/questions/166405/...
罗宾·Baalen

6

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

SET SESSION group_concat_max_len = 100000;

首先要确保您的小组比赛不会超过这里看到的很小的限制。

     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字符在转换中会变得愚蠢。我只是简单地保存了更改前后的列中的数据,这些列我知道这将是内存中的问题,并对其进行比较并生成用于修复数据的更新语句。


4

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.