Скрипт для изменения всех таблиц и полей для сопоставления utf-8-bin в MYSQL - PullRequest
56 голосов
/ 20 сентября 2008

Существует ли сценарий SQL или PHP, который я могу запустить, который изменит параметры сортировки по умолчанию во всех таблицах и полях базы данных?

Я могу написать сам, но я думаю, что это должно быть легко доступно на таком сайте, как этот. Если я смогу придумать его сам, прежде чем кто-то его опубликует, я опубликую его сам.

Ответы [ 16 ]

0 голосов
/ 02 марта 2017

Я обновил ответ nlaq для работы с PHP7 и для правильной обработки многоколоночных индексов, двоичных сопоставленных данных (например, latin1_bin) и т. Д. И немного очистил код. Это единственный код, который я нашел / попробовал, который успешно перенес мою базу данных из latin1 в utf8.

<?php

/////////// BEGIN CONFIG ////////////////////

$username = "";
$password = "";
$db = "";
$host = "";

$target_charset = "utf8";
$target_collation = "utf8_unicode_ci";
$target_bin_collation = "utf8_bin";

///////////  END CONFIG  ////////////////////

function MySQLSafeQuery($conn, $query) {
    $res = mysqli_query($conn, $query);
    if (mysqli_errno($conn)) {
        echo "<b>Mysql Error: " . mysqli_error($conn) . "\n";
        echo "This query caused the above error: " . $query . "\n";
    }
    return $res;
}

function binary_typename($type) {
    $mysql_type_to_binary_type_map = array(
        "VARCHAR" => "VARBINARY",
        "CHAR" => "BINARY(1)",
        "TINYTEXT" => "TINYBLOB",
        "MEDIUMTEXT" => "MEDIUMBLOB",
        "LONGTEXT" => "LONGBLOB",
        "TEXT" => "BLOB"
    );

    $typename = "";
    if (preg_match("/^varchar\((\d+)\)$/i", $type, $mat))
        $typename = $mysql_type_to_binary_type_map["VARCHAR"] . "(" . (2*$mat[1]) . ")";
    else if (!strcasecmp($type, "CHAR"))
        $typename = $mysql_type_to_binary_type_map["CHAR"] . "(1)";
    else if (array_key_exists(strtoupper($type), $mysql_type_to_binary_type_map))
        $typename = $mysql_type_to_binary_type_map[strtoupper($type)];
    return $typename;
}

echo "";

// Connect to database
$conn = mysqli_connect($host, $username, $password);
mysqli_select_db($conn, $db);

// Get list of tables
$tabs = array();
$query = "SHOW TABLES";
$res = MySQLSafeQuery($conn, $query);
while (($row = mysqli_fetch_row($res)) != null)
    $tabs[] = $row[0];

// Now fix tables
foreach ($tabs as $tab) {
    $res = MySQLSafeQuery($conn, "SHOW INDEX FROM `{$tab}`");
    $indicies = array();

    while (($row = mysqli_fetch_array($res)) != null) {
        if ($row[2] != "PRIMARY") {
            $append = true;
            foreach ($indicies as $index) {
                if ($index["name"] == $row[2]) {
                    $index["col"][] = $row[4];
                    $append = false;
                }
            }
            if($append)
                $indicies[] = array("name" => $row[2], "unique" => !($row[1] == "1"), "col" => array($row[4]));
        }
    }

    foreach ($indicies as $index) {
        MySQLSafeQuery($conn, "ALTER TABLE `{$tab}` DROP INDEX `{$index["name"]}`");
        echo "Dropped index {$index["name"]}. Unique: {$index["unique"]}\n";
    }

    $res = MySQLSafeQuery($conn, "SHOW FULL COLUMNS FROM `{$tab}`");
    while (($row = mysqli_fetch_array($res)) != null) {
        $name = $row[0];
        $type = $row[1];
        $current_collation = $row[2];
        $target_collation_bak = $target_collation;
        if(!strcasecmp($current_collation, "latin1_bin"))
            $target_collation = $target_bin_collation;
        $set = false;
        $binary_typename = binary_typename($type);
        if ($binary_typename != "") {
            MySQLSafeQuery($conn, "ALTER TABLE `{$tab}` MODIFY `{$name}` {$binary_typename}");
            MySQLSafeQuery($conn, "ALTER TABLE `{$tab}` MODIFY `{$name}` {$type} CHARACTER SET '{$target_charset}' COLLATE '{$target_collation}'");
            $set = true;
            echo "Altered field {$name} on {$tab} from type {$type}\n";
        }
        $target_collation = $target_collation_bak;
    }

    // Rebuild indicies
    foreach ($indicies as $index) {
         // Handle multi-column indices
         $joined_col_str = "";
         foreach ($index["col"] as $col)
             $joined_col_str = $joined_col_str . ", `" . $col . "`";
         $joined_col_str = substr($joined_col_str, 2);

         $query = "";
         if ($index["unique"])
             $query = "CREATE UNIQUE INDEX `{$index["name"]}` ON `{$tab}` ({$joined_col_str})";
         else
             $query = "CREATE INDEX `{$index["name"]}` ON `{$tab}` ({$joined_col_str})";
         MySQLSafeQuery($conn, $query);

        echo "Created index {$index["name"]} on {$tab}. Unique: {$index["unique"]}\n";
    }

    // Set default character set and collation for table
    MySQLSafeQuery($conn, "ALTER TABLE `{$tab}`  DEFAULT CHARACTER SET '{$target_charset}' COLLATE '{$target_collation}'");
}

// Set default character set and collation for database
MySQLSafeQuery($conn, "ALTER DATABASE `{$db}` DEFAULT CHARACTER SET '{$target_charset}' COLLATE '{$target_collation}'");

mysqli_close($conn);
echo "
"; ?>
0 голосов
/ 06 февраля 2016

Вот простой способ сделать это с помощью просто phpmyadmin, если у вас нет доступа к командной строке или доступа для редактирования INFORMATION_SCHEMA.

Во-первых, прислушайтесь к советам многих других ответов здесь - вы действительно можете здесь все испортить, поэтому сделайте резервную копию. Теперь сделайте резервную копию вашей резервной копии. Также это вряд ли сработает, если ваши данные закодированы не так, как вы их изменяете.

Обратите внимание, что вам нужно будет найти точные имена нарушающей схемы и кодировки символов, которые вы должны изменить перед запуском.

  1. Экспорт базы данных в виде SQL; Сделать копию; Откройте его в любом текстовом редакторе
  2. Сначала найдите и замените схему, например - найдите: latin1_swedish_ci , замените: utf8_general_ci
  3. Найдите и замените кодировки символов, если вам нужно, например, - найти: latin1 , заменить: utf8
  4. Создайте новую тестовую базу данных и загрузите новый файл SQL в phpmyadmin

Это очень простой способ сделать это, но опять же, это не изменит кодировку ваших данных, поэтому он будет работать только при определенных обстоятельствах.

0 голосов
/ 03 ноября 2015

Я думаю, что самый быстрый способ - использовать phpmyadmin и немного jQuery на консоли.

Перейти к структуре таблицы и открыть консоль разработчика Chrome / Firefox (обычно F12 на клавиатуре):

  1. запустите этот код, чтобы выбрать все поля с неправильной кодировкой, и начните изменять:

    var elems = $('dfn'); var lastID = elems.length - 1;
    elems.each(function(i) {
        if ($(this).html() != 'utf8_general_ci') { 
           $('input:checkbox', $('td', $(this).parent().parent()).first()).attr('checked','checked');
        }       
    
        if (i == lastID) {
            $("button[name='submit_mult'][value='change']").click();
        }
    });
    
  2. при загрузке страницы используйте этот код на консоли для выбора правильной кодировки:

    $("select[name*='field_collation']" ).val('utf8_general_ci');
    
  3. сохранение

  4. изменить кодировку таблицы в поле «Сопоставление» на вкладке «Операция»

Протестировано на phpmyadmin 4.0 и 4.4, но я думаю, что работает на всех версиях 4.x

0 голосов
/ 07 июля 2015

Простое (тупое? :) решение с использованием функции множественного выбора в вашей IDE:

  1. запустить "SHOW TABLES"; столбец результатов запроса и копирования (имена таблиц).
  2. мульти-выбор начала и добавить "ALTER TABLE".
  3. выберите несколько концовок и добавьте "CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;"
  4. запускать созданные запросы.
0 голосов
/ 17 июня 2014

Спасибо @nlaq за код, с которого я начал работать над приведенным ниже решением.

Я выпустил плагин для WordPress, не понимая, что WordPress не устанавливает параметры сортировки автоматически. Так что многие люди, использующие плагин, получили latin1_swedish_ci, когда он должен был быть utf8_general_ci.

Вот код, который я добавил в плагин, чтобы обнаружить шаблон latin1_swedish_ci и изменить его на utf8_general_ci.

Протестируйте этот код, прежде чем использовать его в своем плагине!

// list the names of your wordpress plugin database tables (without db prefix)
$tables_to_check = array(
    'social_message',
    'social_facebook',
    'social_facebook_message',
    'social_facebook_page',
    'social_google',
    'social_google_mesage',
    'social_twitter',
    'social_twitter_message',
);
// choose the collate to search for and replace:
$convert_fields_collate_from = 'latin1_swedish_ci';
$convert_fields_collate_to = 'utf8_general_ci';
$convert_tables_character_set_to = 'utf8';
$show_debug_messages = false;
global $wpdb;
$wpdb->show_errors();
foreach($tables_to_check as $table) {
    $table = $wpdb->prefix . $table;
    $indicies = $wpdb->get_results(  "SHOW INDEX FROM `$table`", ARRAY_A );
    $results = $wpdb->get_results( "SHOW FULL COLUMNS FROM `$table`" , ARRAY_A );
    foreach($results as $result){
        if($show_debug_messages)echo "Checking field ".$result['Field'] ." with collat: ".$result['Collation']."\n";
        if(isset($result['Field']) && $result['Field'] && isset($result['Collation']) && $result['Collation'] == $convert_fields_collate_from){
            if($show_debug_messages)echo "Table: $table - Converting field " .$result['Field'] ." - " .$result['Type']." - from $convert_fields_collate_from to $convert_fields_collate_to \n";
            // found a field to convert. check if there's an index on this field.
            // we have to remove index before converting field to binary.
            $is_there_an_index = false;
            foreach($indicies as $index){
                if ( isset($index['Column_name']) && $index['Column_name'] == $result['Field']){
                    // there's an index on this column! store it for adding later on.
                    $is_there_an_index = $index;
                    $wpdb->query( $wpdb->prepare( "ALTER TABLE `%s` DROP INDEX %s", $table, $index['Key_name']) );
                    if($show_debug_messages)echo "Dropped index ".$index['Key_name']." before converting field.. \n";
                    break;
                }
            }
            $set = false;

            if ( preg_match( "/^varchar\((\d+)\)$/i", $result['Type'], $mat ) ) {
                $wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` VARBINARY({$mat[1]})" );
                $wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` VARCHAR({$mat[1]}) CHARACTER SET {$convert_tables_character_set_to} COLLATE {$convert_fields_collate_to}" );
                $set = true;
            } else if ( !strcasecmp( $result['Type'], "CHAR" ) ) {
                $wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` BINARY(1)" );
                $wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` VARCHAR(1) CHARACTER SET {$convert_tables_character_set_to} COLLATE {$convert_fields_collate_to}" );
                $set = true;
            } else if ( !strcasecmp( $result['Type'], "TINYTEXT" ) ) {
                $wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` TINYBLOB" );
                $wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` TINYTEXT CHARACTER SET {$convert_tables_character_set_to} COLLATE {$convert_fields_collate_to}" );
                $set = true;
            } else if ( !strcasecmp( $result['Type'], "MEDIUMTEXT" ) ) {
                $wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` MEDIUMBLOB" );
                $wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` MEDIUMTEXT CHARACTER SET {$convert_tables_character_set_to} COLLATE {$convert_fields_collate_to}" );
                $set = true;
            } else if ( !strcasecmp( $result['Type'], "LONGTEXT" ) ) {
                $wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` LONGBLOB" );
                $wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` LONGTEXT CHARACTER SET {$convert_tables_character_set_to} COLLATE {$convert_fields_collate_to}" );
                $set = true;
            } else if ( !strcasecmp( $result['Type'], "TEXT" ) ) {
                $wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` BLOB" );
                $wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` TEXT CHARACTER SET {$convert_tables_character_set_to} COLLATE {$convert_fields_collate_to}" );
                $set = true;
            }else{
                if($show_debug_messages)echo "Failed to change field - unsupported type: ".$result['Type']."\n";
            }
            if($set){
                if($show_debug_messages)echo "Altered field success! \n";
                $wpdb->query( "ALTER TABLE `$table` MODIFY {$result['Field']} COLLATE $convert_fields_collate_to" );
            }
            if($is_there_an_index !== false){
                // add the index back.
                if ( !$is_there_an_index["Non_unique"] ) {
                    $wpdb->query( "CREATE UNIQUE INDEX `{$is_there_an_index['Key_name']}` ON `{$table}` ({$is_there_an_index['Column_name']})", $is_there_an_index['Key_name'], $table, $is_there_an_index['Column_name'] );
                } else {
                    $wpdb->query( "CREATE UNIQUE INDEX `{$is_there_an_index['Key_name']}` ON `{$table}` ({$is_there_an_index['Column_name']})", $is_there_an_index['Key_name'], $table, $is_there_an_index['Column_name'] );
                }
            }
        }
    }
    // set default collate
    $wpdb->query( "ALTER TABLE `{$table}` DEFAULT CHARACTER SET {$convert_tables_character_set_to} COLLATE {$convert_fields_collate_to}" );
    if($show_debug_messages)echo "Finished with table $table \n";
}
$wpdb->hide_errors();
0 голосов
/ 11 октября 2013

Используйте мою собственную оболочку collatedb , она должна работать:

collatedb <username> <password> <database> <collation>

Пример:

collatedb root 0000 myDatabase utf8_bin
...