如何在 MySQL 5.5 中轻松地将 utf8 表转换为 utf8mb4

geo*_*xis 110 mysql

我有一个现在需要支持 4 字节字符(中文)的数据库。幸运的是,我已经在生产环境中安装了 MySQL 5.5。

所以我只想将所有 utf8_bin 归类为 utf8mb4_bin。

我相信除了一点存储开销之外,这种变化没有性能损失/收益。

Mat*_*ens 136

从我的指南How to support full Unicode in MySQL databases 中,您可以运行以下查询来更新数据库、表或列的字符集和排序规则:

对于每个数据库:

ALTER DATABASE
    database_name
    CHARACTER SET = utf8mb4
    COLLATE = utf8mb4_unicode_ci;
Run Code Online (Sandbox Code Playgroud)

对于每个表:

ALTER TABLE
    table_name
    CONVERT TO CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;
Run Code Online (Sandbox Code Playgroud)

对于每一列:

ALTER TABLE
    table_name
    CHANGE column_name column_name
    VARCHAR(191)
    CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;
Run Code Online (Sandbox Code Playgroud)

(不要盲目复制粘贴!确切的语句取决于列类型、最大长度和其他属性。上面的行只是VARCHAR列的示例。)

但是请注意,您不能完全自动化从utf8到的转换utf8mb4。如上述指南的第 4 步所述,您需要检查列和索引键的最大长度,因为您指定的数字在utf8mb4使用 代替时具有不同的含义utf8

MySQL 5.5 参考手册的第 10.1.11 节有更多关于此的信息。


小智 53

该解决方案将生成并运行转换数据库、表和列所需的查询。它转换类型为varchar, text, tinytext, mediumtext, longtext, 的所有列char

如果出现问题,您应该始终备份数据库

  1. 将以下查询复制到 中gen_queries.sql,将出现的 4 次替换为YOUR_DATABASE_NAME您要转换的数据库的名称:

    USE information_schema;
    SELECT CONCAT("ALTER DATABASE `",table_schema,"` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;") AS _sql
    FROM `TABLES` WHERE table_schema LIKE "YOUR_DATABASE_NAME" AND TABLE_TYPE='BASE TABLE' GROUP BY table_schema UNION
    SELECT CONCAT("ALTER TABLE `",table_schema,"`.`",table_name,"` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;") AS _sql  
    FROM `TABLES` WHERE table_schema LIKE "YOUR_DATABASE_NAME" AND TABLE_TYPE='BASE TABLE' GROUP BY table_schema, table_name UNION
    SELECT CONCAT("ALTER TABLE `",`COLUMNS`.table_schema,"`.`",`COLUMNS`.table_name, "` CHANGE `",column_name,"` `",column_name,"` ",data_type,"(",character_maximum_length,") CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci",IF(is_nullable="YES"," NULL"," NOT NULL"),";") AS _sql 
    FROM `COLUMNS` INNER JOIN `TABLES` ON `TABLES`.table_name = `COLUMNS`.table_name WHERE `COLUMNS`.table_schema like "YOUR_DATABASE_NAME" and data_type in ('varchar','char') AND TABLE_TYPE='BASE TABLE' UNION
    SELECT CONCAT("ALTER TABLE `",`COLUMNS`.table_schema,"`.`",`COLUMNS`.table_name, "` CHANGE `",column_name,"` `",column_name,"` ",data_type," CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci",IF(is_nullable="YES"," NULL"," NOT NULL"),";") AS _sql 
    FROM `COLUMNS` INNER JOIN `TABLES` ON `TABLES`.table_name = `COLUMNS`.table_name WHERE `COLUMNS`.table_schema like "YOUR_DATABASE_NAME" and data_type in ('text','tinytext','mediumtext','longtext') AND TABLE_TYPE='BASE TABLE';
    
    Run Code Online (Sandbox Code Playgroud)
  2. 运行以下命令以生成一个新文件queries.sql,其中包含转换数据库所需的所有查询:

    mysql -u root -p -s < gen_queries.sql > queries.sql
    
    Run Code Online (Sandbox Code Playgroud)
  3. 运行以下命令来运行查询,执行转换:

    mysql -u root -p < queries.sql
    
    Run Code Online (Sandbox Code Playgroud)

笔记:

  • 要在多个数据库上运行转换,请调整table_schema LIKE "YOUR_DATABASE_NAME"查询部分,例如:
    • 替换 withtable_schema LIKE "wiki_%"将转换名称以 开头的所有数据库wiki_
    • 替换为table_type != 'SYSTEM VIEW'将转换所有数据库
  • 我在 mysql 键中的某些 varchar(255) 列中遇到的问题生成了以下错误:
    ERROR 1071 (42000) at line x: Specified key was too long; max key length is 767 bytes
    如果发生这种情况,请将列更改为更小,例如 varchar(150),然后重新运行该命令。
  • 这将排序规则设置为utf8mb4_unicode_ci而不是问题utf8mb4_bin中的要求。根据需要在查询中替换您的首选排序规则。

  • 我必须使用“SET foreign_key_checks = 0;”,然后应用更改,然后“SET foreign_key_checks = 1;”。 (5认同)
  • 我刚刚根据 mrjingles87 的答案创建了一个 bash 脚本:https://github.com/fleio/utf8mb4-convert (4认同)
  • 这是上述脚本的更新版本(非常棒,谢谢!),保留了注释和默认值:https://gist.github.com/mareksuscak/712f6c9e6ead557897d7db164a04f136 (3认同)

小智 8

我使用了以下 shell 脚本。它将数据库名称作为参数并将所有表转换为另一个字符集和排序规则(由脚本中定义的另一个参数或默认值给出)。

#!/bin/bash

# mycollate.sh <database> [<charset> <collation>]
# changes MySQL/MariaDB charset and collation for one database - all tables and
# all columns in all tables

DB="$1"
CHARSET="$2"
COLL="$3"

[ -n "$DB" ] || exit 1
[ -n "$CHARSET" ] || CHARSET="utf8mb4"
[ -n "$COLL" ] || COLL="utf8mb4_general_ci"

echo $DB
echo "ALTER DATABASE \`$DB\` CHARACTER SET $CHARSET COLLATE $COLL;" | mysql

echo "USE \`$DB\`; SHOW TABLES;" | mysql -s | (
    while read TABLE; do
        echo $DB.$TABLE
        echo "ALTER TABLE \`$TABLE\` CONVERT TO CHARACTER SET $CHARSET COLLATE $COLL;" | mysql $DB
    done
)
Run Code Online (Sandbox Code Playgroud)


Roc*_*ite 6

执行以下SQL语句,获取更新字符集和校对规则的结果语句:

\n\n
SET @database = "your_database_name";\n\nSET @charset = "utf8mb4";\n\nSET @collate = "utf8mb4_0900_ai_ci";\n\nSELECT "SET foreign_key_checks = 0;"\nUNION ALL\nSELECT concat(\n  "ALTER DATABASE `",\n  `SCHEMA_NAME`,\n  "` CHARACTER SET = ",\n  @charset,\n  " COLLATE = ",\n  @collate,\n  ";"\n) AS `sql`\nFROM `information_schema`.`SCHEMATA`\nWHERE `SCHEMA_NAME` = @database\n  AND (\n    `DEFAULT_CHARACTER_SET_NAME` <> @charset\n    OR `DEFAULT_COLLATION_NAME` <> @collate\n  )\nUNION ALL\nSELECT concat(\n  "ALTER TABLE `",\n  `TABLE_SCHEMA`,\n  "`.`",\n  `TABLE_NAME`,\n  "` CONVERT TO CHARACTER SET ",\n  @charset,\n  " COLLATE ",\n  @collate,\n  ";"\n) AS `sql`\nFROM `information_schema`.`TABLES`\nWHERE `TABLE_SCHEMA` = @database\n  AND `TABLE_TYPE` = "BASE TABLE"\n  AND `TABLE_COLLATION` <> @collate\nUNION ALL\nSELECT concat(\n  "ALTER TABLE `",\n  c.`TABLE_SCHEMA`,\n  "`.`",\n  c.`TABLE_NAME`,\n  "` CHANGE `",\n  c.`COLUMN_NAME`,\n  "` `",\n  c.`COLUMN_NAME`,\n  "` ",\n  c.`COLUMN_TYPE`,\n  " CHARACTER SET ",\n  @charset,\n  " COLLATE ",\n  @collate,\n  if(c.`IS_NULLABLE`="YES", " NULL", " NOT NULL"),\n  ";"\n) AS `sql`\nFROM `information_schema`.`COLUMNS` c,\n  `information_schema`.`TABLES` t,\n  `information_schema`.`COLLATION_CHARACTER_SET_APPLICABILITY` a\nWHERE c.`TABLE_SCHEMA` = t.`TABLE_SCHEMA`\n  AND c.`TABLE_NAME` = t.`TABLE_NAME`\n  AND t.`TABLE_COLLATION` = a.`COLLATION_NAME`\n  AND c.`TABLE_SCHEMA` = @database\n  AND c.`DATA_TYPE` IN (\n    \'varchar\',\n    \'char\',\n    \'text\',\n    \'tinytext\',\n    \'mediumtext\',\n    \'longtext\'\n  )\n  AND (\n    c.`CHARACTER_SET_NAME` <> a.`CHARACTER_SET_NAME`\n    OR c.`COLLATION_NAME` <> t.`TABLE_COLLATION`\n  )\n  AND t.`TABLE_TYPE` = "BASE TABLE"\nUNION ALL\nSELECT "SET foreign_key_checks = 1;";\n
Run Code Online (Sandbox Code Playgroud)\n\n

注意:

\n\n
    \n
  • 它仅更改与给定字符集或排序规则不一致的数据库、表或列。
  • \n
  • 它不将视图视为常规表。
  • \n
  • 它通过关闭相关检查来避免外键违规。
  • \n
  • 在运行上述 SQL 语句之前相应地更新变量值。\n\n
      \n
    • 默认排序规则utf8mb4_0900_ai_ci可能对您的首选语言不友好。例如,它认为半角括号()与全角括号相同\xef\xbc\x88\xef\xbc\x89,并且在某些情况下会引起麻烦。
    • \n
    • 运行SHOW COLLATION WHERE CHARSET = \'utf8mb4\' AND COLLATION LIKE \'utf8mb4%0900%\';并选择适合您需求的排序规则。
    • \n
  • \n
\n


小智 5

遇到这种情况;这是我用来转换数据库的方法:

  1. 首先,您需要进行编辑my.cnf以使默认数据库连接(应用程序和 MYSQL 之间)兼容 utf8mb4_unicode_ci。如果没有此字符,例如表情符号和应用程序提交的类似字符,将无法以正确的字节/编码进入您的表(除非应用程序的 DB CNN 参数指定 utf8mb4 连接)。

    此处给出说明。

  2. 执行以下 SQL(无需准备 SQL 来更改各个列,ALTER TABLE语句即可做到这一点)。

    在执行以下代码之前,请将“DbName”替换为您的实际数据库名称。

    USE information_schema;
    
    SELECT concat("ALTER DATABASE `",table_schema,
                  "` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;") as _sql
      FROM `TABLES`
     WHERE table_schema like "DbName"
     GROUP BY table_schema;
    
    SELECT concat("ALTER TABLE `",table_schema,"`.`",table_name,
                  "` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;") as _sql
      FROM `TABLES`
     WHERE table_schema like "DbName"
     GROUP BY table_schema, table_name;
    
    Run Code Online (Sandbox Code Playgroud)
  3. 收集上述 SQL 的输出并将其保存在 dot sql 文件中并执行它。

  4. 如果您收到类似有问题的表名的错误#1071 - Specified key was too long; max key length is 1000 bytes.,这意味着该表的某些列上的索引键(应该转换为 MB4 字符串)将非常大,因此 Varchar 列应该 <= 250,以便其索引键最大长度为 1000 字节。检查您有索引的列,如果其中之一是 varchar > 250(最有可能是 255),则

    • 步骤 1:检查该列中的数据,确保该列中的最大字符串大小 <= 250。

      查询示例:

      select `id`,`username`, `email`,
             length(`username`) as l1,
             char_length(`username`) as l2,
             length(`email`) as l3,
             char_length(`email`) as l4
        from jos_users
       order by l4 Desc;
      
      Run Code Online (Sandbox Code Playgroud)
    • 步骤 2:如果索引列数据的最大字符长度 <= 250,则将列长度更改为 250。如果不可能,请删除该列上的索引

    • 步骤 3:然后再次对该表运行 alter table 查询,现在表应该已成功转换为 utf8mb4。

干杯!


Ric*_*mes 3

我会编写一个脚本(用 Perl 或其他语言)来使用 information_schema(表和列)来遍历所有表,并对每个 CHAR/VARCHAR/TEXT 字段执行 MODIFY COLUMN。我会将所有 MODIFY 收集到每个表的单个 ALTER 中;这会更有效率。

我认为(但不确定)Raihan 的建议仅更改了表的默认值。