MySQL 5.5でutf8テーブルをutf8mb4に簡単に変換する方法


71

現在、4バイト文字(中国語)をサポートする必要があるデータベースがあります。幸いなことに、MySQL 5.5はすでに実稼働しています。

したがって、utf8_binからutf8mb4_binまでのすべての照合を作成したいと思います。

この変更によるパフォーマンスの損失/利得は、ストレージのわずかなオーバーヘッド以外にはないと考えています。

回答:


93

私のガイド「MySQLデータベース完全なUnicodeをサポートする方法」から、データベース、テーブル、または列の文字セットと照合順序を更新するために実行できるクエリを次に示します。

各データベースについて:

ALTER DATABASE
    database_name
    CHARACTER SET = utf8mb4
    COLLATE = utf8mb4_unicode_ci;

各テーブルについて:

ALTER TABLE
    table_name
    CONVERT TO CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;

各列について:

ALTER TABLE
    table_name
    CHANGE column_name column_name
    VARCHAR(191)
    CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;

(盲目的にこれをコピーして貼り付けないでください!正確なステートメントは、列のタイプ、最大長、およびその他のプロパティによって異なります。上記の行はVARCHAR列の単なる例です。)

ただし、からutf8への変換を完全に自動化することはできませんutf8mb4上記のガイドのステップ4で説明されているように、指定する数値utf8mb4はの代わりに使用されると異なる意味を持つため、列とインデックスキーの最大長を確認する必要がありますutf8

MySQL 5.5リファレンスマニュアルのセクション10.1.11には、これに関するいくつかの情報があります。


31

いくつかのコマンドを実行してデータベースとテーブルを変換するソリューションがあります。また、タイプのすべての列を変換しvarchartexttinytextmediumtextlongtextchar。また、何かが壊れた場合に備えてデータベースバックアップする必要があります

次のコードをpreAlterTables.sqlというファイルにコピーします。

use information_schema;
SELECT concat("ALTER DATABASE `",table_schema,"` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;") as _sql 
FROM `TABLES` where table_schema like "yourDbName" 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 "yourDbName" group by table_schema, table_name;
SELECT concat("ALTER TABLE `",table_schema,"`.`",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` where table_schema like "yourDbName" and data_type in ('varchar','char');
SELECT concat("ALTER TABLE `",table_schema,"`.`",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` where table_schema like "yourDbName" and data_type in ('text','tinytext','mediumtext','longtext');

「yourDbName」の出現箇所をすべて、変換するデータベースに置き換えます。次に実行します:

mysql -uroot < preAlterTables.sql | egrep '^ALTER' > alterTables.sql

これにより、データベースを変換するために必要なすべてのクエリを含む新しいファイルalterTables.sqlが生成されます。次のコマンドを実行して、変換を開始します。

mysql -uroot < alterTables.sql

table_schemaの条件を変更することで、複数のデータベースを実行するようにこれを調整することもできます。たとえばtable_schema like "wiki_%"、名前がprefixであるすべてのデータベースを変換しますwiki_。すべてのデータベースを変換するには、条件をに置き換えますtable_type!='SYSTEM VIEW'

発生する可能性のある問題。mysqlキーにvarchar(255)列がいくつかありました。これによりエラーが発生します。

ERROR 1071 (42000) at line 2229: Specified key was too long; max key length is 767 bytes

その場合は、varchar(150)のように列を小さくして、コマンドを再実行するだけです。

:この回答は、質問で尋ねられたのutf8mb4_unicode_ci代わりにデータベースを変換しますutf8mb4_bin。しかし、これを単に置き換えることができます。


すばらしいスクリプト、ほんの数メモ。現在のMiariaDbのインストールでは、パスワードを指定する必要があるため、mysql -uroot -pThatrootPassWord < alterTables.sql機能します。そして、すでに述べたように、utf8mb4_binは、とりわけnextcloudが推奨するものです。
ジュリアス

ただし、utf8mb4_0900_ai_ciがデフォルトになりました。monolune.com/ what
Julius

「SET foreign_key_checks = 0;」を使用し、変更を適用してから「SET foreign_key_checks = 1;」を使用する必要がありました。
dfrankow

ありがとうございます これはRedminですべてをutf8mb4に変更するソリューションでした。
Luciano Fantuzzi

5

次のシェルスクリプトを使用しました。データベース名をパラメーターとして受け取り、すべてのテーブルを別の文字セットと照合順序に変換します(別のパラメーターまたはスクリプトで定義された既定値によって与えられます)。

#!/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
)

3

information_schema(TABLESおよびCOLUMNS)を使用してすべてのテーブルをウォークスルーし、すべてのCHAR / VARCHAR / TEXTフィールドでMODUMY COLUMNを実行するスクリプト(Perlなど)を作成します。すべてのMODIFYを各テーブルの単一のALTERに収集します。これはより効率的です。

Raihanの提案は、テーブルのデフォルトを変更するだけだと思います(しかし確信はありません)。


3

この状況に突入しました。データベースを変換するために使用したアプローチは次のとおりです。

  1. まず、my.cnfデフォルトのデータベース接続(アプリケーションとMYSQLの間)utf8mb4_unicode_ciに準拠するように編集する必要があります。アプリが送信した絵文字などのこのような文字がないと、正しいバイト/エンコードでテーブルに到達しません(アプリケーションのDB CNNパラメーターがutf8mb4接続を指定していない限り)。

    ここで与えられる指示。

  2. 次のSQLを実行します(個々の列を変更するために準備されたSQLを取得する必要ALTER TABLEはありません。ステートメントで実行されます)。

    以下のコードを実行する前に、「DbName」を実際のDB名に置き換えます。

    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;
  3. 上記のSQLの出力を収集し、ドットsqlファイルに保存して実行します。

  4. #1071 - Specified key was too long; max key length is 1000 bytes.問題のあるテーブル名とともにエラーが発生した場合、これは、そのテーブルの一部の列(MB4文字列に変換されるはずだった)のインデックスキーが非常に大きくなるため、Varchar列が<= 250である必要があることを意味しますインデックスキーは最大1000バイトです。インデックスがある列をチェックし、それらの1つが250以上(ほとんどの場合255)のvarcharである場合

    • ステップ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;
    • ステップ2:インデックス付き列データの最大文字長が250以下の場合、列の長さを250に変更します。それが不可能な場合は、その列のインデックスを削除します

    • ステップ3:その後、そのテーブルに対してalter tableクエリを再度実行すると、テーブルがutf8mb4に正常に変換されるはずです。

乾杯!


191文字を超える長いVARCHARにインデックスを使用する方法があります。以下を行うには、DBA / SUPER USER権限が必要です。データベースパラメータの設定:innodb_large_prefix:ON; innodb_file_format:バラクーダ。innodb_file_format_max:バラクーダ。
チャウHONGたLiNH

2

私はこのガイドを書きました:http : //hanoian.com/content/index.php/24-automate-the-converting-a-mysql-database-character-set-to-utf8mb4

私の仕事から、データベースとテーブルのALTERでは不十分であることがわかりました。各テーブルに移動し、text / mediumtext / varcharの各列も変更する必要がありました。

幸いなことに、MySQLデータベースのメタデータを検出するスクリプトを作成できたため、テーブルと列をループしてそれらを自動的に変更できました。

MySQL 5.6の長いインデックス:

DBA / SUPER USER権限を持たなければならないことが1つあります。データベースパラメータの設定です。

innodb_large_prefix:ON
innodb_file_format:バラクーダ 
innodb_file_format_max:バラクーダ

この質問の答えには、上記のパラメーターを設定する方法の説明があります:https : //stackoverflow.com/questions/35847015/mysql-change-innodb-large-prefix

もちろん、私の記事にはそれを行うための指示もあります。

MySQLバージョン5.7以降では、innodb_large_prefixはデフォルトでONであり、innodb_file_formatもデフォルトでBarracudaです。


2

この問題を抱えている可能性のある人にとって最良の解決策は、この表に従って、最初に列をバイナリ型に変更することです。

  1. CHAR => BINARY
  2. テキスト=> BLOB
  3. TINYTEXT => TINYBLOB
  4. MEDIUMTEXT => MEDIUMBLOB
  5. LONGTEXT => LONGBLOB
  6. VARCHAR => VARBINARY

その後、列を変更して元の型に戻し、目的の文字セットを使用します。

例えば。:

ALTER TABLE [TABLE_SCHEMA].[TABLE_NAME] MODIFY [COLUMN_NAME] LONGBLOB;
ALTER TABLE [TABLE_SCHEMA].[TABLE_NAME] MODIFY [COLUMN_NAME] VARCHAR(140) CHARACTER SET utf8mb4;

いくつかのlatin1テーブルで試してみましたが、すべての発音区別符号が保持されていました。

これを行うすべての列に対してこのクエリを抽出できます。

SELECT
CONCAT('ALTER TABLE ', TABLE_SCHEMA,'.', TABLE_NAME,' MODIFY ', COLUMN_NAME,' VARBINARY;'),
CONCAT('ALTER TABLE ', TABLE_SCHEMA,'.', TABLE_NAME,' MODIFY ', COLUMN_NAME,' ', COLUMN_TYPE,' CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;')
FROM information_schema.columns
WHERE TABLE_SCHEMA IN ('[TABLE_SCHEMA]')
AND COLUMN_TYPE LIKE 'varchar%'
AND (COLLATION_NAME IS NOT NULL AND COLLATION_NAME NOT LIKE 'utf%');

0

多かれ少なかれこれを自動的に行うスクリプト作成しました

<?php
/**
 * Requires php >= 5.5
 * 
 * Use this script to convert utf-8 data in utf-8 mysql tables stored via latin1 connection
 * This is a PHP port from: https://gist.github.com/njvack/6113127
 *
 * BACKUP YOUR DATABASE BEFORE YOU RUN THIS SCRIPT!
 *
 * Once the script ran over your databases, change your database connection charset to utf8:
 *
 * $dsn = 'mysql:host=localhost;port=3306;charset=utf8';
 * 
 * DON'T RUN THIS SCRIPT MORE THAN ONCE!
 *
 * @author hollodotme
 *
 * @author derclops since 2019-07-01
 *
 *         I have taken the liberty to adapt this script to also do the following:
 *
 *         - convert the database to utf8mb4
 *         - convert all tables to utf8mb4
 *         - actually then also convert the data to utf8mb4
 *
 */

header('Content-Type: text/plain; charset=utf-8');

$dsn      = 'mysql:host=localhost;port=3306;charset=utf8';
$user     = 'root';
$password = 'root';
$options  = [
    \PDO::ATTR_CURSOR                   => \PDO::CURSOR_FWDONLY,
    \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
    \PDO::MYSQL_ATTR_INIT_COMMAND       => "SET CHARACTER SET latin1",
];


$dbManager = new \PDO( $dsn, $user, $password, $options );

$databasesToConvert = [ 'database1',/** database3, ... */ ];
$typesToConvert     = [ 'char', 'varchar', 'tinytext', 'mediumtext', 'text', 'longtext' ];

foreach ( $databasesToConvert as $database )
{
    echo $database, ":\n";
    echo str_repeat( '=', strlen( $database ) + 1 ), "\n";

    $dbManager->exec( "USE `{$database}`" );

    echo "converting database to correct locale too ... \n";

    $dbManager->exec("ALTER DATABASE `{$database}` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci");


    $tablesStatement = $dbManager->query( "SHOW TABLES" );
    while ( ($table = $tablesStatement->fetchColumn()) )
    {
        echo "Table: {$table}:\n";
        echo str_repeat( '-', strlen( $table ) + 8 ), "\n";

        $columnsToConvert = [ ];

        $columsStatement = $dbManager->query( "DESCRIBE `{$table}`" );

        while ( ($tableInfo = $columsStatement->fetch( \PDO::FETCH_ASSOC )) )
        {
            $column = $tableInfo['Field'];
            echo ' * ' . $column . ': ' . $tableInfo['Type'];

            $type = preg_replace( "#\(\d+\)#", '', $tableInfo['Type'] );

            if ( in_array( $type, $typesToConvert ) )
            {
                echo " => must be converted\n";

                $columnsToConvert[] = $column;
            }
            else
            {
                echo " => not relevant\n";
            }
        }


        //convert table also!!!
        $convert = "ALTER TABLE `{$table}` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci";

        echo "\n", $convert, "\n";
        $dbManager->exec( $convert );
        $databaseErrors = $dbManager->errorInfo();
        if( !empty($databaseErrors[1]) ){
            echo "\n !!!!!!!!!!!!!!!!! ERROR OCCURED ".print_r($databaseErrors, true)." \n";
            exit;
        }


        if ( !empty($columnsToConvert) )
        {
            $converts = array_map(
                function ( $column )
                {
                    //return "`{$column}` = IFNULL(CONVERT(CAST(CONVERT(`{$column}` USING latin1) AS binary) USING utf8mb4),`{$column}`)";
                    return "`{$column}` = CONVERT(BINARY(CONVERT(`{$column}` USING latin1)) USING utf8mb4)";
                },
                $columnsToConvert
            );

            $query = "UPDATE IGNORE `{$table}` SET " . join( ', ', $converts );

            //alternative
            // UPDATE feedback SET reply = CONVERT(BINARY(CONVERT(reply USING latin1)) USING utf8mb4) WHERE feedback_id = 15015;


            echo "\n", $query, "\n";


            $dbManager->exec( $query );

            $databaseErrors = $dbManager->errorInfo();
            if( !empty($databaseErrors[1]) ){
                echo "\n !!!!!!!!!!!!!!!!! ERROR OCCURED ".print_r($databaseErrors, true)." \n";
                exit;
            }
        }

        echo "\n--\n";
    }

    echo "\n";
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.