Rails 使用 Rails Migration 转换 MySQL 数据库和表的字符集总结

grantbb · 2017年09月20日 · 最后由 greatghoul 回复于 2017年09月21日 · 2633 次阅读

MySQL Character Set 基础知识

对于 MySQL 数据库你可以在不同的 Level 设置 Character Set 和 Collation,包括:Server Level,Database Level,Table Level,Column Level 还有 Application Level.

Server Level:

可以通过命令行设置,也可以通过配置文件设置

默认: --character-set-server=latin1   

latin1_swedish_ci is the default collation for latin1

还可以通过重新编译时指定参数实现:use the DEFAULT_CHARSET and DEFAULT_COLLATION

作用范围:如果创建数据库时不指定,那么就使用 Server Level 的设置

查看当前的设定,可以查看系统变量:character_set_server and collation_server

Database Level:

可以在创建数据库时设置:

CREATE DATABASE db_name CHARACTER SET latin1 COLLATE latin1_swedish_ci;

默认值:可以由 character_set_database and collation_database 系统变量决定。

可以通过下面的命令查看当前的设置:

USE db_name;

SELECT @@character_set_database, @@collation_database;

或者

SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = 'db_name';

作用:如果建表时没有指定,那么会作为表的默认值,同时也是作为 LOAD DATA 的默认值

修改数据库 level 的 character set 和 collation

ALTER DATABASE db_name CHARACTER SET utf8 COLLATE utf8_unicode_ci;

# 或者单独修改
ALTER DATABASE my_database DEFAULT COLLATE utf8_unicode_ci;

ALTER DATABASE my_database DEFAULT CHARACTER SET utf8;

Table Level:

可以在建表的语句中进行设置

作用:如果字段没有具体制定,那么会作为字段的默认值 note:该功能是 mysql 的一个扩展,不是标准的 SQL

使用下面语句可以同时修改 table 和 table 中字段的设置

ALTER TABLE tbl_name CONVERT TO CHARACTER SET charset_name [COLLATE collation_name];

查看某个数据库中的所有表的一些设置信息的语句

SHOW TABLE STATUS FROM db_name;

Column Level:

N/A

Application Connection Level:

对于 Rails 应用,在 database.yml 的数据库连接设置中加上 ?reconnect=true&encoding=utf8&collation=utf8_unicode_ci

查看数据库不同级别的元数据的设置的语句,比如:character set

SELECT DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA S WHERE schema_name = 'db_name' AND DEFAULT_COLLATION_NAME != 'utf8_unicode_ci';

SELECT TABLE_NAME, TABLE_COLLATION FROM information_schema.TABLES WHERE table_schema = 'db_name' AND table_collation != 'utf8_unicode_ci';

SELECT * FROM information_schema.COLUMNS WHERE table_schema = 'db_name' AND collation_name != 'utf8_unicode_ci';

ConvertDatabaseCharacterSetAndCollationToUtf8 Migration

class ConvertDatabaseCharacterSetAndCollationToUtf8 < ActiveRecord::Migration
  def up
    execute <<~SQL
      ALTER DATABASE #{ActiveRecord::Base.connection.current_database} CHARACTER SET utf8 COLLATE utf8_unicode_ci;
    SQL
  end

  def down
    execute <<~SQL
      ALTER DATABASE #{ActiveRecord::Base.connection.current_database} CHARACTER SET latin1 COLLATE latin1_swedish_ci;
    SQL
  end
end

ConvertTablesCharacterSetAndCollationToUtf8 Migration

class ConvertTablesCharacterSetAndCollationToUtf8 < ActiveRecord::Migration
  def up
    execute("SET foreign_key_checks = 0")

    latin_tables_sql = <<~SQL
      SELECT TABLE_NAME, TABLE_COLLATION
      FROM information_schema.TABLES
      WHERE table_schema = '#{ActiveRecord::Base.connection.current_database}'
      AND table_collation != 'utf8_unicode_ci';
    SQL

    results = ActiveRecord::Base.connection.execute(latin_tables_sql)
    say "Total: #{results.count}"

    results.each do |result|
      alter_table_sql = "ALTER TABLE #{result[0]} CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;"
      execute alter_table_sql
    end

    execute("SET foreign_key_checks = 1")
  end
end

Rails Migration tips

在调试该功能的时候,学到的一些 tips:

rake db:migrate:status 查看当前 migration 的状态,包括版本信息等

rake db:migrate VERSION=33333333 migrate 指定 version

rake db:rollback STEP=n 通过 STEP 参数指定回滚的范围

User.connection 可以用来检查当前数据库设置和连接的信息

ActiveRecord::Base.connection.current_database 获取当前连接的数据库

ActiveRecord::Migrator.current_version 查看当前的版本

在 Rails Console 或者 Runner 中执行 SQL 语句,可以使用 ActiveRecord::Migration.execute("SQL")

参考文章: How to change all columns' and tables' collation to 'utf8_bin' in MySQL

MySQL 的坑:默认不是 utf8mb4

doitian 回复

我们这是很多年前的数据库了,根据需要一步一步来吧。

doitian 回复

之前查了下转换的资料,然后匹配迁移到 pg

需要 登录 后方可回复, 如果你还没有账号请 注册新账号