どうも問題は、MySQLというかUTF-8自身に問題があるようだ

UTF-8というのは、統制がとれていないんだ。そこで変換プログラムが間違った変換をしてしまう可能性があるか。納得。

文字コードの自動変換機能による弊害(文字化け) †

4.0 までは、キャラクターセットはサーバーだけが設定するものであり、クライアントがサーバーのキャラクターセットに自動的に合せて動作していました。

ところが

4.1 になってから、サーバー、クライアントがそれぞれのキャラクターセットで動作するようになりました。

例えば、

    * クライアントが sjis で動作
    * サーバーが ujis で動作

している場合、サーバーは、クライアントに対しては常に sjis のデータを送るようになります。

サーバーは ujis のデータを sjis に変換して、クライアントに送るのです。

これは一見便利です。

が、落とし穴があります。

   1. 変換できないキャラクターセットの組み合わせの場合
   2. 変換しきれない文字があった場合

こういう場合は、文字が ? になったり、期待外れの文字になったりします。

例えば、

    * クライアントが latin1 で動作
    * サーバーが sjis で動作

の場合、どうやって sjis を latin1 の文字コードに変換できるというのでしょう?
出来るわけがないので、sjis 文字はあえなく全て ? になってしまいます。
(え、「?」にしなけりゃいいのにって?
私もそう思います。変換できなければ「?」にせず、そのままのバイトのままにしておけば クライアントが latin1 でも問題が少なかったかもしれません。が、仕様が「?」に変換なので、なんとも。)

例えば、

Unicode を使用してアプリを作っている方は経験されているでしょうが、 Unicode の変換マップというのは統一が取れていません。
同じことが MySQL にもおきています。
MySQL が変換するコードと、あなたが使用している環境が考えるコードと文字の形の組み合わせが一致するとは限りません。

では、クライアントが latin1 で、サーバーが ujis,sjis の状況というのは、起きうるものなのでしょうか?

困ったことに、現状では簡単にこの状態が引き起こってしまいます。

    * あなたが使用している PHP は、自分でコンパイルしましたか?
    * あなたが使用している MySQL は、ご自身の手でコンパイルしましたか?

ほとんどの方が、他の誰かがコンパイルしたものを使用していると思います。
では、

    * あなたが使用している PHP, MySQL のライブラリ(libmysql.dll, libmysqlclient)の標準キャラクターセットは何かご存じですか?

そう、ここが問題なのです。
日本人以外のデベロッパーが作ったバイナリは、ujis,sjis を標準のキャラクターセットにしているわけがありません。
事実 MySQL AB 配布のバイナリは、latin1 が標準です。
それらのバイナリを使って PHPMySQL モジュールを動かせば(作成すれば)、クライアント(PHP)は latin1 で動作し、サーバーは ujis,sjis で動くことになるのです。こうして日本語文字は破壊されます。

これらの問題を避けるには、方法は

    * PHP(Ruby,Perl,C,...)のアプリの変更。キャラクターセットを mysql_options()で指定する or アプリがmy.cnf を読むようにする
    * PHP(Ruby,Perl,C,...)の MySQL モジュールの標準キャラクターセットを、自分が使うキャラクターセットにする。これはアプリの変更はない。しかし、libmysql.dll, libmysqlclient のコンパイルし直しが発生する。



参考:
http://www.mysql.gr.jp/frame/modules/bwiki/?FAQ#content_1_40