it-swarm.asia

Cara mudah mengkonversi tabel utf8 ke utf8mb4 di MySQL 5.5

Saya memiliki database yang sekarang perlu mendukung 4 byte karakter (bahasa Cina). Untungnya saya sudah memiliki MySQL 5.5 dalam produksi.

Jadi saya hanya ingin membuat semua koleksi yang utf8_bin menjadi utf8mb4_bin.

Saya percaya tidak ada kehilangan/perolehan kinerja dengan perubahan ini selain sedikit overhead penyimpanan.

91
geoaxis

Dari panduan saya Cara mendukung Unicode penuh dalam database MySQL , berikut adalah pertanyaan yang dapat Anda jalankan untuk memperbarui charset dan susunan basis data, tabel, atau kolom:

Untuk setiap basis data:

ALTER DATABASE
    database_name
    CHARACTER SET = utf8mb4
    COLLATE = utf8mb4_unicode_ci;

Untuk setiap tabel:

ALTER TABLE
    table_name
    CONVERT TO CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;

Untuk setiap kolom:

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

(Jangan sembarang menyalin-tempel ini! Pernyataan yang tepat tergantung pada jenis kolom, panjang maksimum, dan properti lainnya. Baris di atas hanyalah contoh untuk VARCHAR kolom.)

Namun, perhatikan bahwa Anda tidak dapat mengotomatisasi sepenuhnya konversi dari utf8 hingga utf8mb4. Seperti yang dijelaskan dalam langkah 4 dari panduan di atas , Anda harus memeriksa panjang maksimum kolom dan kunci indeks, karena angka yang Anda tentukan memiliki arti yang berbeda ketika utf8mb4 digunakan sebagai ganti utf8.

Bagian 10.1.11 dari Manual Referensi MySQL 5.5 memiliki beberapa informasi lebih lanjut tentang ini.

106
Mathias Bynens

Saya punya solusi yang akan mengonversi basis data dan tabel dengan menjalankan beberapa perintah. Itu juga mengonversi semua kolom dari jenis varchar, text, tinytext, mediumtext, longtext, char. Anda juga harus membuat cadangan basis data Anda jika terjadi kerusakan.

Salin kode berikut ke file yang bernama 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');

Ganti semua kemunculan "yourDbName" dengan database yang ingin Anda konversi. Lalu lari:

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

Ini akan menghasilkan file baru alterTables.sql, dengan semua pertanyaan yang Anda butuhkan untuk mengonversi database. Jalankan perintah berikut untuk memulai konversi:

mysql -uroot < alterTables.sql

Anda juga dapat mengadaptasi ini untuk dijalankan melalui banyak basis data, dengan mengubah kondisi untuk table_schema. Sebagai contoh table_schema like "wiki_%" akan mengonversi semua database dengan awalan nama wiki_. Untuk mengonversi semua basis data, ganti ketentuan dengan table_type!='SYSTEM VIEW'.

Suatu masalah yang mungkin muncul. Saya memiliki beberapa kolom varchar (255) di kunci mysql. Ini menyebabkan kesalahan:

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

Jika itu terjadi, Anda cukup mengubah kolom menjadi lebih kecil, seperti varchar (150), dan jalankan kembali perintah.

Harap dicatat: Jawaban ini mengubah database menjadi utf8mb4_unicode_ci dari pada utf8mb4_bin, ditanyakan dalam pertanyaan. Tetapi Anda bisa langsung mengganti ini.

39
MrJingles87

Saya menggunakan skrip Shell berikut. Dibutuhkan nama database sebagai parameter dan mengubah semua tabel ke charset dan collation lain (diberikan oleh parameter lain atau nilai default yang didefinisikan dalam skrip).

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

Saya akan menulis skrip (dalam Perl, atau apa pun) untuk menggunakan information_schema (TABLES dan COLUMNS) untuk menelusuri semua tabel, dan melakukan MODIFY COLUMN pada setiap bidang CHAR/VARCHAR/TEXT. Saya akan mengumpulkan semua MODIFY menjadi ALTER tunggal untuk setiap tabel; ini akan lebih efisien.

Saya pikir (tapi tidak yakin) bahwa saran Raihan hanya mengubah default untuk tabel.

3
Rick James

Berlari ke dalam situasi ini; inilah pendekatan yang saya gunakan untuk mengonversi basis data saya:

  1. Pertama, Anda perlu mengedit my.cnf untuk membuat koneksi database default (antara aplikasi dan MYSQL) sesuai utf8mb4_unicode_ci. Tanpa karakter ini seperti emoji dan yang serupa dikirimkan oleh aplikasi Anda tidak akan membuatnya ke tabel Anda dalam byte kanan/encoding (kecuali params DB CNN aplikasi Anda menentukan koneksi utf8mb4).

    Instruksi diberikan di sini .

  2. Jalankan SQL berikut (tidak perlu bersiap-siap SQL untuk mengubah kolom individu, ALTER TABLE pernyataan akan melakukan itu).

    Sebelum Anda menjalankan kode di bawah ini, ganti "DbName" dengan nama DB Anda yang sebenarnya.

    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. Kumpulkan dan simpan output dari SQL di atas dalam file dot sql dan jalankan.

  4. Jika Anda mendapatkan kesalahan seperti #1071 - Specified key was too long; max key length is 1000 bytes. bersama dengan nama tabel yang bermasalah, ini berarti kunci indeks pada beberapa kolom dari tabel itu (yang seharusnya dikonversi ke MB4 charstring) akan sangat besar sehingga kolom Varchar harus <= 250 sehingga kunci indeksnya akan menjadi maks 1000 byte. Periksa kolom di mana Anda memiliki indeks dan jika salah satunya adalah varchar> 250 (kemungkinan 255)

    • Langkah 1: periksa data di kolom itu untuk memastikan bahwa ukuran string maksimal dalam kolom itu adalah <= 250.

      Contoh permintaan:

      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;
      
    • Langkah 2: jika panjang karakter maksimum dari data kolom yang diindeks <= 250 kemudian ubah panjang col menjadi 250. jika itu tidak memungkinkan, hapus indeks pada kolom itu

    • Langkah 3: kemudian jalankan query tabel alter untuk tabel itu lagi dan tabel sekarang harus dikonversi menjadi utf8mb4 berhasil.

Bersulang!

3
Nav44

Untuk orang-orang yang mungkin memiliki masalah ini solusi terbaik adalah memodifikasi terlebih dahulu kolom ke tipe biner, menurut tabel ini:

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

Dan setelah itu ubah kolom kembali ke tipe sebelumnya dan dengan charset yang Anda inginkan.

Misalnya.:

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

Saya mencoba beberapa tabel latin1 dan menyimpan semua diakritik.

Anda dapat mengekstrak kueri ini untuk semua kolom yang melakukan ini:

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%');
2
MalachiteBR

Saya menulis panduan ini: http://hanoian.com/content/index.php/24-automate-the-converting-a-mysql-database-character-set-to-utf8mb4

Dari pekerjaan saya, saya melihat bahwa ALTER database dan tabel tidak cukup. Saya harus pergi ke setiap tabel dan ALTER masing-masing teks/mediumtext/varchar kolom juga.

Untungnya saya bisa menulis skrip untuk mendeteksi metadata dari database MySQL, sehingga bisa loop melalui tabel dan kolom dan MENGUBAH mereka secara otomatis.

Indeks panjang untuk MySQL 5.6:

Ada satu hal yang harus Anda lakukan sebagai hak istimewa DBA/SUPER USER: Mengatur parameter basis data:

 innodb_large_prefix: ON 
 innodb_file_format: Barracuda 
 innodb_file_format_max: Barracuda 

Dalam jawaban untuk pertanyaan ini, ada instruksi cara mengatur parameter di atas: https://stackoverflow.com/questions/35847015/mysql-change-innodb-large-prefix

Tentu saja, dalam artikel saya, ada instruksi untuk melakukannya juga.

Untuk MySQL versi 5.7 atau lebih baru , innodb_large_prefix AKTIF secara default, dan innodb_file_format juga merupakan Barracuda secara default.

2

Saya membuat skrip yang melakukan ini lebih atau kurang secara otomatis:

<?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";
}
0
clops