search
LoginSignup
147

More than 5 years have passed since last update.

posted at

updated at

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

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 | キリト    | パパ         |
+----+-----------+--------------+

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

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
147