it-swarm.asia

كيفية تحويل جداول utf8 بسهولة إلى utf8mb4 في MySQL 5.5

لدي قاعدة بيانات تحتاج الآن لدعم 4 أحرف بايت (الصينية). لحسن الحظ لدي بالفعل MySQL 5.5 في الإنتاج.

لذا أود فقط أن أقوم بجميع عمليات الترتيب التي هي utf8_bin إلى utf8mb4_bin.

أعتقد أنه لا توجد خسارة/ربح في الأداء مع هذا التغيير بخلاف القليل من حمل التخزين.

91
geoaxis

من دليلي كيفية دعم Unicode الكامل في قواعد بيانات MySQL ، إليك الاستعلامات التي يمكنك تشغيلها لتحديث مجموعة الأحرف وتنسيق قاعدة بيانات أو جدول أو عمود:

لكل قاعدة بيانات:

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.

القسم 10.1.11 من دليل MySQL 5.5 المرجعي يحتوي على مزيد من المعلومات حول ذلك.

106
Mathias Bynens

لدي حل يقوم بتحويل قواعد البيانات والجداول عن طريق تشغيل بعض الأوامر. كما أنه يحول جميع الأعمدة من النوع varchar ، text ، tinytext ، mediumtext ، longtext ، char. يجب عليك أيضًا النسخ الاحتياطي لقاعدة البيانات الخاصة بك في حالة حدوث شيء ما.

انسخ الكود التالي إلى ملف يسمى 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 like "wiki_%" جميع قواعد البيانات ببادئة الاسم wiki_. لتحويل جميع قواعد البيانات ، استبدل الشرط بـ table_type!='SYSTEM VIEW'.

قضية قد تنشأ. كان لدي بعض أعمدة 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 ، في السؤال. ولكن يمكنك ببساطة استبدال هذا.

39
MrJingles87

استخدمت النص البرمجي لشل التالي. يأخذ اسم قاعدة البيانات كمعلمة ويحول جميع الجداول إلى مجموعة أحرف وتجميع أخرى (يتم توفيرها بواسطة معلمات أخرى أو قيمة افتراضية محددة في البرنامج النصي).

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

أود أن أكتب برنامجًا نصيًا (في Perl ، أو أيا كان) لاستخدام information_schema (الجداول والأعمدة) للتجول في جميع الجداول ، وإجراء تعديل في كل حقل CHAR/VARCHAR/TEXT. أود أن أجمع جميع MODIFYs في ALTER واحد لكل جدول ؛ هذا سيكون أكثر كفاءة.

أعتقد (ولكن لست متأكدًا) أن اقتراح ريحان يغير فقط افتراضي للجدول.

3
Rick James

واجهت هذا الوضع ؛ إليك الطريقة التي استخدمتها لتحويل قاعدة بياناتي:

  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 أعلاه في ملف 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;
      
    • الخطوة 2: إذا كان الحد الأقصى لطول بيانات العمود المفهرس <= 250 ، فقم بتغيير طول العمود إلى 250. إذا لم يكن ذلك ممكنًا ، فقم بإزالة الفهرس في ذلك العمود

    • الخطوة 3: ثم قم بتشغيل استعلام تغيير الجدول لهذا الجدول مرة أخرى ويجب تحويل الجدول الآن إلى utf8mb4 بنجاح.

في صحتك!

3
Nav44

بالنسبة للأشخاص الذين قد يواجهون هذه المشكلة ، فإن أفضل حل هو تعديل الأعمدة أولاً إلى نوع ثنائي ، وفقًا لهذا الجدول:

  1. CHAR => ثنائي
  2. النص => النقطة
  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%');
2
MalachiteBR

كتبت هذا الدليل: http://hanoian.com/content/index.php/24-automate-the-converting-a-mysql-database-character-set-to-utf8mb4

من عملي ، رأيت أن ALTER قاعدة البيانات والجداول ليست كافية. كان علي أن أذهب إلى كل جدول وأقوم بتغيير ALTER لكل من أعمدة النص/النص المتوسط ​​/ varchar أيضًا.

لحسن الحظ ، تمكنت من كتابة برنامج نصي لاكتشاف البيانات الوصفية لقواعد بيانات MySQL ، بحيث يمكن أن يتكرر خلال الجداول والأعمدة ويغيرها تلقائيًا.

فهرس طويل لـ MySQL 5.6:

هناك شيء واحد يجب أن يكون لديك امتياز DBA/SUPER USER للقيام به: تعيين معلمات قاعدة البيانات:

 innodb_large_prefix: ON 
 innodb_file_format: Barracuda 
 innodb_file_format_max: Barracuda 

في إجابات هذا السؤال ، هناك تعليمات حول كيفية تعيين هذه المعلمات أعلاه: https://stackoverflow.com/questions/35847015/mysql-change-innodb-large-prefix

بالطبع ، في مقالتي ، هناك تعليمات للقيام بذلك أيضًا.

بالنسبة إلى MySQL الإصدار 5.7 أو الأحدث ، يتم تشغيل innodb_large_prefix بشكل افتراضي ، ويكون innodb_file_format هو Barracuda أيضًا بشكل افتراضي.

2
Châu Hồng Lĩnh

أنا صنعت نصًا برمجيًا والذي يفعل ذلك بشكل أو بآخر تلقائيًا:

<?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