MySQL 5.5'te utf8 tablolarını utf8mb4 dosyalarına kolayca dönüştürme


71

Şimdi 4 bayt karakteri (Çince) desteklemek için gereken bir veritabanı var. Neyse ki zaten üretimde MySQL 5.5 var.

Bu yüzden utf8_bin ile utf8mb4_bin arasındaki tüm harmanlamaları yapmak istiyorum.

Bu değişiklikle ilgili bir miktar depolama yükünden başka bir performans kaybı / kazanç olmadığına inanıyorum.

Yanıtlar:


93

Kılavuzumdan MySQL veritabanlarında tam Unicode nasıl desteklenir , işte veritabanı, tablo veya sütunun karakter kümesini ve harmanlamasını güncellemek için çalıştırabileceğiniz sorular:

Her veritabanı için:

ALTER DATABASE
    database_name
    CHARACTER SET = utf8mb4
    COLLATE = utf8mb4_unicode_ci;

Her tablo için:

ALTER TABLE
    table_name
    CONVERT TO CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;

Her sütun için:

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

(Körelince kopyala-yapıştır yapmayın! Exact ifadesi sütun türüne, maksimum uzunluğa ve diğer özelliklere bağlıdır. Yukarıdaki satır bir VARCHARsütun için sadece bir örnektir .)

Tam dönüşümü otomatik hale getiremeyiz Ancak, Not utf8için utf8mb4. Yukarıda belirtilen kılavuzun 4. adımında açıklandığı gibi, belirttiğiniz sayı utf8mb4yerine kullanıldığında farklı bir anlama sahip olduğundan maksimum sütun uzunluğunu ve dizin tuşlarını kontrol etmeniz gerekir utf8.

MySQL 5.5 Başvuru Kılavuzunun Bölüm 10.1.11’inde bu konuda daha fazla bilgi bulunmaktadır.


31

Birkaç komut çalıştırarak veritabanlarını ve tabloları dönüştürecek bir çözüme sahibim. Ayrıca türdeki tüm sütunları dönüştürür varchar, text, tinytext, mediumtext, longtext, char. Ayrıca , bir şey bozulursa veritabanınızı da yedeklemelisiniz .

Aşağıdaki kodu preAlterTables.sql adlı bir dosyaya kopyalayın:

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" öğesinin tüm oluşumlarını dönüştürmek istediğiniz veritabanı ile değiştirin. O zaman koş:

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

Bu, veritabanını dönüştürmek için ihtiyacınız olan tüm sorgularla birlikte alterTables.sql dosyasını oluşturur. Dönüştürmeyi başlatmak için aşağıdaki komutu çalıştırın:

mysql -uroot < alterTables.sql

Bunu ayrıca table_schema koşulunu değiştirerek birden fazla veritabanında çalışacak şekilde uyarlayabilirsiniz. Örneğin table_schema like "wiki_%", tüm veritabanlarını ad öneki ile dönüştürür wiki_. Tüm veritabanlarını dönüştürmek için koşulu ile değiştirin table_type!='SYSTEM VIEW'.

Ortaya çıkabilecek bir sorun. MySQL anahtarlarında bazı varchar (255) sütunlar vardı. Bu bir hataya neden olur:

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

Bu durumda, sütunu varchar (150) gibi daha küçük olacak şekilde değiştirebilir ve komutu yeniden çalıştırabilirsiniz.

Lütfen dikkat : Bu cevap , sorulan soru utf8mb4_unicode_ciyerine veritabanını dönüştürür utf8mb4_bin. Ancak bunu basitçe değiştirebilirsiniz.


Harika komut dosyası, sadece birkaç not; Mevcut MiariaDb kurulumları parolanın verilmesini gerektirir, bu yüzden mysql -uroot -pThatrootPassWord < alterTables.sqlçalışır. Daha önce de belirtildiği gibi utf8mb4_bin, diğerlerinin yanı sıra nextcloud'un önerdiği şey.
Julius

ama utf8mb4_0900_ai_ci şimdi varsayılandır, bkz. monolune.com/what-is-the-utf8mb4_0900_ai_ci-collation
Julius

"SET foreign_key_checks = 0;" kullanmak zorunda kaldım, sonra değişiklikleri uyguladım, sonra "SET foreign_key_checks = 1;"
dfrankow

Teşekkür ederim dostum. Bu, hepsini utf8mb4 olarak değiştirmek için Redmin'deki çözümdü.
Luciano Fantuzzi

5

Aşağıdaki kabuk betiğini kullandım. Veritabanı adını parametre olarak alır ve tüm tabloları başka bir karakter kümesine ve harmanlamaya dönüştürür (başka bir parametre tarafından veya komut dosyasında tanımlanan varsayılan değer tarafından verilir).

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

Tüm tablolarda dolaşmak için information_schema'yı (TABLES ve COLUMNS) kullanmak ve her CHAR / VARCHAR / TEXT alanında MODIFY COLUMN yapmak için bir betik (Perl'de veya her neyse) yazardım. Tüm DEĞİŞİKLİKLERI her tablo için tek bir ALTER olarak toplardım; bu daha verimli olacak.

Raihan'ın önerisinin sadece masa için varsayılanı değiştirdiğini düşünüyorum (ama emin değilim) .


3

Bu duruma rastladım; veritabanımı dönüştürmek için kullandığım yaklaşım:

  1. Öncelikle, my.cnfvarsayılan veritabanı bağlantısını (uygulamalar ve MYSQL arasında) utf8mb4_unicode_ci ile uyumlu hale getirmek için düzenlemelisiniz . Bu karakterler olmadan emojiler ve uygulamalarınız tarafından gönderilen benzerler, sağ bayt / kodlamada tablolarınıza uymayacak (uygulamanızın DB CNN paragrafları bir utf8mb4 bağlantısı belirtmediği sürece).

    Burada verilen talimatlar .

  2. Aşağıdaki SQL'i yürütün (ayrı sütunları değiştirmek için hazırlanmış SQL'e gerek yok, ALTER TABLEifadeler bunu yapacak).

    Aşağıdaki kodu çalıştırmadan önce "DBName" yerine gerçek DB adınızı yazınız.

    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. Bir nokta sql dosyasında yukarıdaki SQL çıktısını toplayın ve kaydedin ve yürütün.

  4. #1071 - Specified key was too long; max key length is 1000 bytes.Sorunlu tablo adıyla birlikte olduğu gibi bir hata alırsanız , bu, o tablonun bazı sütunlarında (MB4 charstring'e dönüştürülmesi gereken) indeks anahtarı anlamına gelir; bu nedenle Varchar sütununun <= 250 olması gerekir. Dizin anahtarı en fazla 1000 bayt olacaktır. Dizinleri olan sütunları kontrol edin ve bunlardan biri varchar> 250 (büyük olasılıkla 255) ise

    • Adım 1: Bu sütundaki maksimum dize boyutunun <= 250 olduğundan emin olmak için bu sütundaki verileri kontrol edin.

      Örnek sorgu:

      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;
    • Adım 2: Dizine alınmış sütun verilerinin maksimum uzunluğu <= 250 ise, sütun uzunluğunu 250 olarak değiştirin. Bu mümkün değilse, bu sütundaki dizini kaldırın

    • Adım 3: daha sonra bu tablo için alter table sorgusunu tekrar çalıştırın ve tablo başarılı bir şekilde utf8mb4'e dönüştürülmelidir.

Şerefe!


191 karakterden uzun VARCHAR için indeks kullanmanın bir yolu var. Bunu yapmak için DBA / SUPER USER ayrıcalıklarına sahip olmalısınız: Veritabanı parametrelerini ayarlama: innodb_large_prefix: ON; innodb_file_format: Barracuda; innodb_file_format_max: Barracuda;
Châu Hồng Lĩnh

2

Bu kılavuzu yazdım: http://hanoian.com/content/index.php/24-automate-the-converting-a-mysql-database-character-set-to-utf8mb4

Çalışmamdan itibaren, ALTER veritabanının ve tabloların yeterli olmadığını gördüm. Her tabloya girip her bir metin / mediumtext / varchar sütununa ALTER girmem gerekti.

Neyse ki MySQL veritabanlarının meta verilerini algılamak için bir komut dosyası yazabildim, böylece tablolar ve sütunlar arasında dolaşıp bunları otomatik olarak ALTERE edinebiliyordu.

MySQL 5.6 için uzun dizin:

Yapma ayrıcalıklarına sahip DBA / SUPER USER adlı kişinin yapması gereken bir şey var: Veritabanı parametrelerini ayarlama:

innodb_large_prefix: AÇIK
innodb_file_format: Barracuda 
innodb_file_format_max: Barracuda

Bu sorunun cevaplarında, bu parametrelerin nasıl ayarlanacağına dair bir talimat var: https://stackoverflow.com/questions/35847015/mysql-change-innodb-large-prefix

Tabii ki, makalemde, bunu yapmak için de talimatlar var.

MySQL sürüm 5.7 veya daha yenisi için , innodb_large_prefix, varsayılan olarak AÇIK'tır ve innodb_file_format, varsayılan olarak Barracuda'dır.


2

Bu soruna sahip olan insanlar için en iyi çözüm, bu tabloya göre ilk önce sütunları ikili bir türe dönüştürmektir:

  1. CHAR => İKİLİ
  2. METİN => BLOB
  3. TINYTEXT => TINYBLOB
  4. MEDIUMTEXT => MEDIUMBLOB
  5. LONGTEXT => LONGBLOB
  6. VARCHAR => ÇEŞİTLİ

Ve bundan sonra sütunu eski tipine ve istediğiniz karakter kümesi ile değiştirin.

Örneğin.:

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

Birkaç latin1 tabloda denedim ve tüm aksan tuttu.

Bu sorguyu, bunu yapan tüm sütunlar için çıkarabilirsiniz:

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

Bunu otomatik olarak aşağı yukarı yapan bir komut dosyası yaptım :

<?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";
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.