Edited at

ActiveRecordでデフォルトの照合順序を変更する

More than 3 years have passed since last update.

MySQLには文字列の照合順序(collation)というのがあって、MySQL側でのcharset utf8のときのデフォルトの照合順序はutf8_general_ciです。

ActiveRecord::Migrationでは明示的に照合順序を指定しない場合、charset utf8で照合順序utf8_unicode_ciのデータベースを作成しますが、これは少なくとも日本語圏では多くの人が期待する挙動ではないと思われるので注意が必要です。

たとえば、以下のようなファミリーテーブルをrake db:migrateすると


db/migrate/20121112110702_create_families.rb

# coding: utf-8

class CreateFamilies < ActiveRecord::Migration
def change
create_table :families do |t|
t.string :name
t.string :relationship
end
# add_index :families, :relationship, unique: true
Family.create(name: 'ユイ', relationship: '本人')
Family.create(name: 'キリト', relationship: 'パパ')
Family.create(name: 'アスナ', relationship: 'ハハ')
end
end

utf8_unicode_ciなテーブルが作成されます。

CREATE TABLE `families` (

`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`relationship` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

SELECT * FROM families;

+----+-----------+--------------+
| id | name | relationship |
+----+-----------+--------------+
| 1 | ユイ | 本人 |
| 2 | キリト | パパ |
| 3 | アスナ | ハハ |
+----+-----------+--------------+

ここで'パパ'を検索すると、utf8_unicode_ciでは'ハ'と'パ'は等価と扱われるので'ハハ'も検索に引っかかります。この挙動を期待するケースはあまり多くないのではないでしょうか。

SELECT * FROM families WHERE relationship = 'パパ';

+----+-----------+--------------+
| id | name | relationship |
+----+-----------+--------------+
| 2 | キリト | パパ |
| 3 | アスナ | ハハ |
+----+-----------+--------------+

また、relationshipカラムにUNIQUEインデックスを張ってあると'パパ'と'ハハ'がUNIQUE制約を満たせないので以下のようにエラーになります。

Mysql2::Error: Duplicate entry 'ハハ' for key 'relationship': INSERT INTO `families` (`name`, `relationship`) VALUES ('アスナ', 'ハハ')

ということで、照合順序utf8_general_ciなデータベースを作成したいわけですが、これはコンフィグでcharsetcollationを指定するだけで簡単にいけます。


config/database.yml

development:

adapter: mysql2
encoding: utf8
charset: utf8
collation: utf8_general_ci
reconnect: false
database: sao_development

これでrake db:migrateすると

CREATE TABLE `families` (

`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`relationship` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `relationship` (`relationship`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8

MySQL側のデフォルトの照合順序(utf8_general_ci)で作成されて

SELECT * FROM families WHERE relationship = 'パパ';

+----+-----------+--------------+
| id | name | relationship |
+----+-----------+--------------+
| 2 | キリト | パパ |
+----+-----------+--------------+

ちゃんと'パパ'だけが検索に引っかかりました!

これで家族におばあちゃんが増えても安心ですね!!