MySQLで4バイトutf-8文字を扱う&ファイルフォーマットを変更する
エントリをアップするの忘れてて今更感。
そもそも4バイトutf-8って何?
- 絵文字
- なんか特殊な漢字
が該当します。
4バイトutf-8文字の例
𣖔:𣖔木作(ほうのきざく、福島県の地名) 𣗄:𣗄代(たらのきだい、山形県の地名) 𩸽:ホッケ(おいしいさかな) 🍣:寿司(おいしい) 🍺:ビール(おいしい)
参考文献
JIS第3水準漢字一覧表【全1259字】(JIS X 0213:2004) - fragment.database.
JIS第4水準漢字一覧表【全2436字】(JIS X 0213:2004) - fragment.database.
「文字コード技術入門」こぼれ話: 「ほうの木作」を訪ねる - yanok.net
MySQLで扱えないの?
結論から言うと、文字セットを「utf8mb4_*」にすればOKです。
「utf8_*」では使えません。(INSERTしてみると、4バイト文字以降の部分が消えます)
接続方法、データベース、テーブル、カラム、すべてこの設定に統一しましょう。
何らかの理由で一部のみを変えたい場合、その動作はよく検証してください。
例えば、接続設定のみを変更した場合は4バイト文字が「?」に化けますが、それ以降の文字列は消えずに済みます。
接続設定とカラムのみの変更でも4バイト文字を保存することは可能ではあるようですが、
一時テーブル作成時、わざわざ文字セットを指定しない限りはデータベースの設定値が使用されるなど、
トラブルの原因になりがちです。
utf8mb4のデメリットはないの?
utf8_*系であれば1文字に割り当てられるサイズが3バイトですが、
utf8mb4_*系にすることで4バイトになります。
つまり文字列(特にCHAR型)のカラムやインデックスのサイズが大きくなったりします。
それは困る
困ると思う人はもうご存知な気もしますが、
ファイルフォーマットをAntelopeからBarracudaに変更することをお勧めします。
ちょっと何言ってるかわかんない
参考文献を貼っておきます。
InnoDBの制限とファイルフォーマットAntelopeとBarracudaの違い - かみぽわーる
MySQL InnoDBファイルフォーマットBarracudaへの設定方法 | Go-Nextブログ
Barracudaにすると何が良いのかというと、
- インデックスサイズ制限が拡張できる(innodb_large_prefix)
- 可変長カラムが外部ページに保存され、ローカルページにはそこへのポインタだけ保存される
- Antelopeでは先頭768バイトがローカルページに保存されていた。つまりBarracudaにすると軽くなる
まあ後者は4バイト文字とは関係ないんですが、前者が良いポイントですね。
元々767バイトという制限だったのが3072バイトまで拡張可能になるんです。
(そんな長い文字列にインデックス張るのがよくない、という話は置いといて)
utf8_*系では255文字のカラムに対してインデックスを張ろうとすると
「255文字×3バイト=765バイト」で767バイト制限に引っかからなかったんですが、
これをutf8mb4_*系に変更するだけで1020バイトになり、エラーとなってしまうのです。
変更方法
データベースのデフォルト文字コードを変更
こちらは一発。
ALTER DATABASE 【データベース名】 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
テーブル・カラムの文字コードを変更
こちらはちょっと面倒。
(新規で作るときは、もちろんデータベースの設定さえしておけば無指定でOKなんですが)
後述のクエリやExcelを駆使して自動生成するのがたぶん一番楽。
ALTER TABLE 【テーブル名】 -- テーブルの文字コード DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci -- 以降はカラムの文字コード -- (型やコメントなども含める必要があるため注意) ,CHANGE 【カラム名1】 【カラム名1】 VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'カラム1' ,CHANGE 【カラム名2】 【カラム名2】 VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT 'カラム2' , ...
参考:作業時の確認用クエリ
データベースの文字コードを確認する
SELECT `SCHEMA_NAME` ,`DEFAULT_CHARACTER_SET_NAME` ,`DEFAULT_COLLATION_NAME` FROM `information_schema`.`SCHEMATA` WHERE `SCHEMA_NAME` = '【データベース名】';
文字コードが「utf8」系になっているテーブルを抽出する
SELECT `TABLE_NAME` ,`TABLE_COLLATION` ,`TABLE_COMMENT` FROM `information_schema`.`TABLES` WHERE `TABLE_SCHEMA` = '【データベース名】' AND `TABLE_COLLATION` LIKE 'utf8\_%';
文字コードが「utf8」系になっているカラムを抽出する
SELECT `TABLE_NAME` ,`COLUMN_NAME` ,`COLUMN_TYPE` ,`CHARACTER_SET_NAME` ,`COLLATION_NAME` ,`COLUMN_COMMENT` FROM `information_schema`.`COLUMNS` WHERE `TABLE_SCHEMA` = '【データベース名】' AND `CHARACTER_SET_NAME` = 'utf8';