読者です 読者をやめる 読者になる 読者になる

MySQLの文字コード事情

この前 MySQL Casual に登壇して、「MySQLの文字コード事情」と称して発表してきました。

終電の都合で途中退席しましたが楽しかったです。また機会があれば参加したいです。

発表スライドはこちら

www.slideshare.net

以下、補足のような何か。

「Charset≒エンコーディング (MySQLに限らない)」

英語版のWikipediaでもcharsetCharacter encoding にリダイレクトされます。

自分がcharsetという用語に出会ったのはおそらくメールのContent-Typeヘッダが初めてだったと思います。 今ではメールだけではなくHTTPのヘッダでも使用されています。

なお、CharsetはInternet Assigned Numbers Authority(IANA)という組織で管理されています。http://www.iana.org/assignments/character-sets/character-sets.xhtml

ujis

MySQLで日本語を使用できるようになった時にEUC-JPエンコーディングのcharsetの名前をujisとしてしまったのは自分です。ゴメンナサイ。

いや、今でこそEUC-JPの方がメジャーでujisなんてもう見かけないんですけど、1998年当時はeucJPとujisのどちらも使われたんですよ。(あと ujis と sjis で韻を踏んでいていいかなーとか…)

EUCは Extended Unix Code の略で、ujisは Unixized JIS の略です。ググってみたら ujis の方が EUC-JP よりも歴史は古いようですね。

「歴史的には、まず「日本語EUC」の元 (ujis) があって、その工夫を I18N 的枠組に拡張したものが EUC」

http://naruse.hateblo.jp/entry/20090308/1236517235

「ふつうはutf8mb4」

とスライドには書きましたが、cp932を使うメリットも無いことはないです。

UTF-8は日本語の文字は普通3バイトですがCP932は2バイトです。 つまりディスクやメモリの消費量がUTF-8に比べて2/3で済むということです。

扱える文字が Windows-31J の範囲の文字だけで良くて、少しでも資源を節約したいのであれば、cp932を使用するのもいいかもしれません。

🍣=🍺問題とCollation

伝統的にMySQLは標準で大文字小文字を区別しないので、ci(Case Insensitive)を使いたくなってしまうのですけど、PostgreSQL とかは普通に大文字小文字は別の文字扱いだし、実は MySQL も utf8mb4_bin でも全然問題ないのかもしれません。

utf8mb4_bin であれば🍣=🍺問題も、ハハ=パパ問題も発生しませんし。

‘🍣’=‘🍺’=‘�’

MySQLで同じ文字とみなされるかどうかは WEIGHT_STRING() という関数の戻り値が同じかどうかで確かめられます。

SET NAMES utf8mb4 COLLATE utf8mb4_general_ci;
SELECT HEX(WEIGHT_STRING('🍣'));  -- => FFFD
SELECT HEX(WEIGHT_STRING('🍺'));  -- => FFFD
SELECT HEX(WEIGHT_STRING('�'));  -- => FFFD

SET NAMES utf8mb4 COLLATE utf8mb4_unicode_520_ci;
SELECT HEX(WEIGHT_STRING('🍣'));  -- => FBC3F363
SELECT HEX(WEIGHT_STRING('🍺'));  -- => FBC3F37A

SET NAMES utf8mb4 COLLATE utf8mb4_bin;
SELECT HEX(WEIGHT_STRING('🍣'));  -- => 01F363
SELECT HEX(WEIGHT_STRING('🍺'));  -- => 01F37A

utf8mb4_bin の場合は Unicode のコードポイントがそのまま使用されるようです。

「'パ'=‘パ'」と「'パ’ LIKE ‘パ'」

半角カナの「パ」は「ハ」と「゚」の二文字で構成されていますが、unicode_ci では一文字の「パ」と一致します。

SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;
SELECT 'パ'='パ'; -- => 1

ですが、LIKE では一致しません。

SELECT 'パ' LIKE 'パ'; -- => 0

SQL 標準では、LIKE は文字ごとに一致を実行するため、= 比較演算子とは異なる結果が生成される可能性があります。

https://dev.mysql.com/doc/refman/5.6/ja/string-comparison-functions.html#operator_like

…ということのようです。

Unicode Collation Algorithm

そのうち書きたい(書くとは言ってない)。

書いた