Mysql
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Mysql

Nâng cấp Laravel 5.4, chuyển đổi sang utf4mb từ utf8

Được rồi, tôi đã viết một bản di chuyển để đạt được điều này cho hệ thống của riêng mình.

  • Nó cho phép bạn tùy chọn chỉ định tên kết nối để tham chiếu đến một kết nối khác với kết nối mặc định.

  • Nó lấy danh sách các bảng từ cơ sở dữ liệu của kết nối bằng cách sử dụng SHOW TABLES truy vấn.

  • Sau đó, nó lặp qua từng bảng và cập nhật tất cả các cột kiểu chuỗi / ký tự thành bộ ký tự và đối chiếu mới.

  • Tôi đã thực hiện nó để một lệnh gọi lại phải được cung cấp để xác định xem một cột có nên thay đổi độ dài của nó thành độ dài mới được cung cấp hay không. Trong quá trình triển khai của tôi, VARCHARCHAR các cột có độ dài lớn hơn 191 được cập nhật để có độ dài 191 trong quá trình di chuyển lên và VARCHARCHAR các cột có độ dài chính xác là 191 được cập nhật để có độ dài 255 khi di chuyển ngược / xuống.

  • Khi tất cả các cột chuỗi / ký tự đã được cập nhật, một vài truy vấn sẽ được chạy để thay đổi bộ ký tự và đối chiếu của bảng, chuyển đổi bất kỳ đối chiếu nào còn lại thành ký tự mới và sau đó để thay đổi bộ ký tự và đối chiếu mặc định của bảng.

  • Cuối cùng, bộ ký tự và đối chiếu mặc định của cơ sở dữ liệu sẽ được thay đổi.

Ghi chú

  • Ban đầu, tôi đã cố gắng đơn giản chuyển đổi các bảng sang mã hóa mới nhưng lại gặp sự cố với độ dài cột. 191 ký tự là độ dài ký tự tối đa trong utf8mb4 khi sử dụng InnoDB trong phiên bản MySQL / MariaDB của tôi và thay đổi đối chiếu bảng đã dẫn đến lỗi.

  • Lúc đầu, tôi chỉ muốn cập nhật độ dài thành độ dài mới nhưng tôi cũng muốn cung cấp tính năng khôi phục, vì vậy đây không phải là một tùy chọn vì trong phương pháp ngược lại, tôi sẽ đặt độ dài của cột là utf8mb4 thành 255, sẽ là quá lâu, vì vậy tôi cũng chọn thay đổi đối chiếu.

  • Sau đó, tôi đã cố gắng chỉ thay đổi độ dài, bộ ký tự và đối chiếu của varcharchar cột quá dài, nhưng trong hệ thống của tôi, điều này dẫn đến lỗi khi tôi có chỉ mục nhiều cột bao gồm các cột như vậy. Rõ ràng, các chỉ mục nhiều cột phải sử dụng cùng một đối chiếu.

  • Một lưu ý quan trọng về điều này là quá trình di chuyển ngược / xuống sẽ không hoàn hảo 100% cho tất cả mọi người. Tôi không nghĩ rằng có thể làm như vậy mà không lưu trữ thêm thông tin về các cột ban đầu khi di chuyển. Vì vậy, triển khai hiện tại của tôi cho việc di chuyển ngược / xuống là giả định rằng các cột có độ dài 191 ban đầu là 255.

  • Một lưu ý quan trọng tương tự về điều này là điều này sẽ thay đổi một cách mù quáng các đối chiếu của tất cả các cột chuỗi / ký tự thành đối chiếu mới, bất kể đối chiếu ban đầu là gì, vì vậy nếu có các cột có các đối chiếu khác nhau, tất cả chúng sẽ được chuyển đổi thành đối chiếu mới và điều ngược lại sẽ làm giống nhau, bản gốc sẽ không được giữ nguyên.

<?php

use Illuminate\Database\Migrations\Migration;

class UpgradeDatabaseToUtf8mb4 extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        $this->changeDatabaseCharacterSetAndCollation('utf8mb4', 'utf8mb4_unicode_ci', 191, function ($column) {
            return $this->isStringTypeWithLength($column) && $column['type_brackets'] > 191;
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        $this->changeDatabaseCharacterSetAndCollation('utf8', 'utf8_unicode_ci', 255, function ($column) {
            return $this->isStringTypeWithLength($column) && $column['type_brackets'] == 191;
        });
    }

    /**
     * Change the database referred to by the connection (null is the default connection) to the provided character set
     * (e.g. utf8mb4) and collation (e.g. utf8mb4_unicode_ci). It may be necessary to change the length of some fixed
     * length columns such as char and varchar to work with the new encoding. In which case the new length of such
     * columns and a callback to determine whether or not that particular column should be altered may be provided. If a
     * connection other than the default connection is to be changed, the string referring to the connection may be
     * provided as the last parameter (This string will be passed to DB::connection(...) to retrieve an instance of that
     * connection).
     *
     * @param string       $charset
     * @param string       $collation
     * @param null|int     $newColumnLength
     * @param Closure|null $columnLengthCallback
     * @param string|null  $connection
     */
    protected function changeDatabaseCharacterSetAndCollation($charset, $collation, $newColumnLength = null, $columnLengthCallback = null, $connection = null)
    {
        $tables = $this->getTables($connection);

        foreach ($tables as $table) {
            $this->updateColumnsInTable($table, $charset, $collation, $newColumnLength, $columnLengthCallback, $connection);
            $this->convertTableCharacterSetAndCollation($table, $charset, $collation, $connection);
        }

        $this->alterDatabaseCharacterSetAndCollation($charset, $collation, $connection);
    }

    /**
     * Get an instance of the database connection provided with an optional string referring to the connection. This
     * should be null if referring to the default connection.
     *
     * @param string|null $connection
     *
     * @return \Illuminate\Database\Connection
     */
    protected function getDatabaseConnection($connection = null)
    {
        return DB::connection($connection);
    }

    /**
     * Get a list of tables on the provided connection.
     *
     * @param null $connection
     *
     * @return array
     */
    protected function getTables($connection = null)
    {
        $tables = [];

        $results = $this->getDatabaseConnection($connection)->select('SHOW TABLES');
        foreach ($results as $result) {
            foreach ($result as $key => $value) {
                $tables[] = $value;
                break;
            }
        }

        return $tables;
    }

    /**
     * Given a stdClass representing the column, extract the required information in a more accessible format. The array
     * returned will contain the field name, the type of field (Without the length), the length where applicable (or
     * null), true/false indicating the column allowing null values and the default value.
     *
     * @param stdClass $column
     *
     * @return array
     */
    protected function extractInformationFromColumn($column)
    {
        $type = $column->Type;
        $typeBrackets = null;
        $typeEnd = null;

        if (preg_match('/^([a-z]+)(?:\\(([^\\)]+?)\\))?(.*)/i', $type, $matches)) {
            $type = strtolower(trim($matches[1]));

            if (isset($matches[2])) {
                $typeBrackets = trim($matches[2]);
            }

            if (isset($matches[3])) {
                $typeEnd = trim($matches[3]);
            }
        }

        return [
            'field' => $column->Field,
            'type' => $type,
            'type_brackets' => $typeBrackets,
            'type_end' => $typeEnd,
            'null' => strtolower($column->Null) == 'yes',
            'default' => $column->Default,
            'charset' => is_string($column->Collation) && ($pos = strpos($column->Collation, '_')) !== false ? substr($column->Collation, 0, $pos) : null,
            'collation' => $column->Collation
        ];
    }

    /**
     * Tell if the provided column is a string/character type and needs to have it's charset/collation changed.
     *
     * @param string $column
     *
     * @return bool
     */
    protected function isStringType($column)
    {
        return in_array(strtolower($column['type']), ['char', 'varchar', 'tinytext', 'text', 'mediumtext', 'longtext', 'enum', 'set']);
    }

    /**
     * Tell if the provided column is a string/character type with a length.
     *
     * @param string $column
     *
     * @return bool
     */
    protected function isStringTypeWithLength($column)
    {
        return in_array(strtolower($column['type']), ['char', 'varchar']);
    }

    /**
     * Update all of the string/character columns in the database to be the new collation. Additionally, modify the
     * lengths of those columns that have them to be the newLength provided, when the shouldUpdateLength callback passed
     * returns true.
     *
     * @param string        $table
     * @param string        $charset
     * @param string        $collation
     * @param int|null      $newLength
     * @param Closure|null  $shouldUpdateLength
     * @param string|null   $connection
     */
    protected function updateColumnsInTable($table, $charset, $collation, $newLength = null, Closure $shouldUpdateLength = null, $connection = null)
    {
        $columnsToChange = [];

        foreach ($this->getColumnsFromTable($table, $connection) as $column) {
            $column = $this->extractInformationFromColumn($column);

            if ($this->isStringType($column)) {
                $sql = "CHANGE `%field%` `%field%` %type%%brackets% CHARACTER SET %charset% COLLATE %collation% %null% %default%";
                $search = ['%field%', '%type%', '%brackets%', '%charset%', '%collation%', '%null%', '%default%'];
                $replace = [
                    $column['field'],
                    $column['type'],
                    $column['type_brackets'] ? '(' . $column['type_brackets'] . ')' : '',
                    $charset,
                    $collation,
                    $column['null'] ? 'NULL' : 'NOT NULL',
                    is_null($column['default']) ? ($column['null'] ? 'DEFAULT NULL' : '') : 'DEFAULT \'' . $column['default'] . '\''
                ];

                if ($this->isStringTypeWithLength($column) && $shouldUpdateLength($column) && is_int($newLength) && $newLength > 0) {
                    $replace[2] = '(' . $newLength . ')';
                }

                $columnsToChange[] = trim(str_replace($search, $replace, $sql));
            }
        }

        if (count($columnsToChange) > 0) {
            $query = "ALTER TABLE `{$table}` " . implode(', ', $columnsToChange);

            $this->getDatabaseConnection($connection)->update($query);
        }
    }

    /**
     * Get a list of all the columns for the provided table. Returns an array of stdClass objects.
     *
     * @param string      $table
     * @param string|null $connection
     *
     * @return array
     */
    protected function getColumnsFromTable($table, $connection = null)
    {
        return $this->getDatabaseConnection($connection)->select('SHOW FULL COLUMNS FROM ' . $table);
    }

    /**
     * Convert a table's character set and collation.
     *
     * @param string      $table
     * @param string      $charset
     * @param string      $collation
     * @param string|null $connection
     */
    protected function convertTableCharacterSetAndCollation($table, $charset, $collation, $connection = null)
    {
        $query = "ALTER TABLE {$table} CONVERT TO CHARACTER SET {$charset} COLLATE {$collation}";
        $this->getDatabaseConnection($connection)->update($query);

        $query = "ALTER TABLE {$table} DEFAULT CHARACTER SET {$charset} COLLATE {$collation}";
        $this->getDatabaseConnection($connection)->update($query);
    }

    /**
     * Change the entire database's (The database represented by the connection) character set and collation.
     *
     * # Note: This must be done with the unprepared method, as PDO complains that the ALTER DATABASE command is not yet
     *         supported as a prepared statement.
     *
     * @param string      $charset
     * @param string      $collation
     * @param string|null $connection
     */
    protected function alterDatabaseCharacterSetAndCollation($charset, $collation, $connection = null)
    {
        $database = $this->getDatabaseConnection($connection)->getDatabaseName();

        $query = "ALTER DATABASE {$database} CHARACTER SET {$charset} COLLATE {$collation}";

        $this->getDatabaseConnection($connection)->unprepared($query);
    }
}

Làm ơn, vui lòng sao lưu cơ sở dữ liệu của bạn trước khi chạy cái này . Sử dụng và tự chịu rủi ro!



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. MySQL chỉ định thứ tự tùy ý theo id

  2. Cách xử lý cơ sở dữ liệu kế thừa trong khuôn khổ Django

  3. Codeigniter:Cột 'id' trong mệnh đề thứ tự không rõ ràng

  4. Cài đặt mysqldb trên Snow Leopard

  5. Ví dụ DAYOFMONTH () - MySQL