2005-08-13

MySQLの実験(3) - 漢字の使用

本章ではこれまでに文字セットに関するシステム変数や、データベースでの文字セットの扱いについて実験してきました。ここではクライアントが使用する文字セットについての実験を行います。

クライアントの文字セットに関する詳細は以下のURLを参照して下さい。

クライアント文字セット

Perl/CGIなどのMySQLクライアントでは、漢字を使用する場合、常にクライアント文字セットを意識する必要があります。基本的には、正しいクライアント文字セットを使用すれば漢字の使用ができます。クライアント文字セットとデータベース(サーバ側)が使用している文字セットの変換に関しては、一般に言われているように、機種依存文字やマッピングの違いによる問題が発生しますが、これはMySQL固有の問題ではありません。可能な限りクライアント文字セットとデータベース文字セットは同じにする方が良いですが、実行環境によってはできない場合もあります。ここでは、クライアント文字セットがsjis、データベース文字セットがutf8の場合を例にとって実験を進めていきます。

charset.gif

クライアント文字セットに関係するシステム変数は次の3つです。

  • character_set_client
  • character_set_connection
  • character_set_results

これらのシステム変数を正しく設定すれば、漢字の使用ができます。右にクライアント−サーバ間の文字セットの変換イメージを図示します。それぞれの変数については「システム変数の参照と変更−文字セット関連のシステム変数」も参照して下さい。

大雑把に言うとクライアント文字セットと結果文字セットをPerl/CGIを書いているソースコードの文字セット(例えばsjis)に設定して、接続文字セットには「適当なもの」を選択します。「適当なもの」とは漢字を扱っている文字セット(utf8/ujis/sjisなど)を意味します。漢字を扱っていない文字セットを指定してはいけません。

しかし、先に実験したように、クライアント文字セットに関係する3つのシステム変数には全てlatin1などの非漢字コードが設定されている場合があります。

MySQLではこれらのシステム変数の設定を行うSQL文が2つ用意してあります。

SET NAMES

構文: SET NAMES { 文字セット名 | DEFAULT }

3つのセッション変数(character_set_client, character_set_results, character_set_connection)を指定の文字セットに設定します。筆者の実験では、DEFAULTを指定した場合、グローバル変数character_set_clientの値が3つのセッション変数に設定されました。SET NAMES は実質的に以下と等価です。

set character_set_client = 文字セット名;
set character_set_results = 文字セット名;
set character_set_connection = 文字セット名;

SET CHARACTER SET

構文: SET CHARACTER SET {文字セット名 | DEFAULT}

3つのセッション変数(character_set_client, character_set_results, character_set_connection)を設定します。character_set_clientとcharacter_set_resultsは指定の文字セットに、character_set_connectionはcharacter_set_databaseの値に設定します。筆者の実験では、DEFAULTを指定した場合、グローバル変数character_set_clientの値がcharacter_set_clientとcharacter_set_resultsに、character_set_connectionはcharacter_set_databaseの値に設定されました。SET CHARACTER SETは実質的に以下と等価です。

set character_set_client = 文字セット名;
set character_set_results = 文字セット名;
set character_set_connection = @@character_set_database;


SET NAMESとSET CHARACTER SETの違いは接続文字セット(character_set_connection)の扱いです。上図(文字セット変換のイメージ)を見て下さい。例えば、クライアント文字セット(A)をデータベース文字セット(B)に変換する場合を考えます。両者の間に位置する接続文字セット(C)は、SET NAMESでは(A)に設定され、SET CHARACTER SETでは(B)に設定されます。この違いはどこにあるのでしょうか?筆者の実験では、両者の違いを明確に指摘するような答えは見つかりませんでした。以下ではSET NAMESの方を使用して実験を進めます。

実験の準備

以下の実験で使用する文字セットは、特に明記していない限り、クライアント(Perl/CGI)側はsjis、データベース(サーバ側)はutf8です。また、テーブル名、カラム名に漢字は使用しません。

実験に先立ち、テスト用のテーブルを作成します。ブラウザから http://localhost/mysql/sqlfile.cgi?kanji0.sql を実行します。

kanji0.sql

# テーブル作成
DROP TABLE IF EXISTS syain;
CREATE TABLE syain (
  syain_no int(10) NOT NULL,
  syain_name varchar(50),
  bumon_no int(10),
  PRIMARY KEY (syain_no)
);

SET NAMESによるクライアント文字セットの指定

ブラウザから http://localhost/mysql/sqlfile.cgi?kanji1.sql を実行します。

kanji1.sql

#SET NAMES sjis;
SHOW VARIABLES LIKE 'character\_set\_%';

# テーブルにデータを挿入
DELETE FROM syain;
INSERT INTO syain VALUES(1,'織田信長',3);
INSERT INTO syain VALUES(2,'豊臣秀吉',1);
INSERT INTO syain VALUES(3,'徳川家康',2);

# テーブルの検索
SELECT * FROM syain;

実行結果

perl-exp21a.gif

上の実行結果から分かるように、クライアント文字セット関連の3つのシステム変数がlatin1のままでは正しい文字セット変換ができません。変換できない文字は?に変換されています。

SQLスクリプトファイル(kanji1.sql)の1行目のコメントを外し、

SET NAMES sjis;

を実行すると以下の結果になります。

実行結果

perl-exp21b.gif

今度は正しく文字セットが変換されています。SET CHARACTER SET sjisを使用した場合は、character_set_connectionがutf8になりますが、変換は正しく行われます。

プログラムによる漢字コードの変換

今後は少し考え方を変えて、クライアント側とサーバ側の文字セット間の変換を(Jcode.pmなどを使用して)プログラムで行う場合を考えてみます。本章ではサンプルプログラム(sqlfile.cgi)を利用して実験しているので、実際のプログラムによる変換はできません。従って、SQLスクリプトファイルを利用してシミュレーションする事を試みます。

ブラウザから http://localhost/mysql/sqlfile.cgi?kanji2.sql を実行します。

kanji2.sql

SET NAMES utf8;
SHOW VARIABLES LIKE 'character\_set\_%';

# テーブルにデータを挿入
DELETE FROM syain;
INSERT INTO syain VALUES(1,_sjis'織田信長',3);
INSERT INTO syain VALUES(2,_sjis'豊臣秀吉',1);
INSERT INTO syain VALUES(3,_sjis'徳川家康',2);

# テーブルの検索
SET character_set_results = sjis;
SELECT * FROM syain;

上のSQLスクリプトでは漢字変換のシミュレーションを以下の方法で行っています。

  • クライアント側からサーバ側への文字セット変換には、文字セットイントロデューサ(_sjis)を使います。
  • サーバ側からクライアント側への文字セット変換には、SET character_set_results = sjis を使います。

MySQLでは文字セットイントロデューサによって文字列リテラルの文字セットを指定する事ができます。Jcode.pmを使った実際のプログラミングでは文字セットの変換は以下のようになります。

use Jcode;
Jcode::convert(\$field, 'utf8', 'sjis'); #クライアント側からサーバ側
Jcode::convert(\$field, 'sjis', 'utf8'); #サーバ側からクライアント側

この方法では、SET NAMES utf8を使ってクライアント側とサーバ側の文字セットを同じに設定して、文字列カラムに現れる漢字コードを自前で変換します。この方法は、sjisとutf8のAsciiコード部分が同じ1バイトのコードで表現されている事を利用しています。テーブル名やカラム名に漢字を使うと、この方法は使えません。この場合は、SQL文を全て変換する必要があります。

サーバ側の文字セットをクライアント側に合わせる方法

サーバ側(データベース:正確にはテーブル)の文字セットをクライアント側に合わせます。  ブラウザから http://localhost/mysql/sqlfile.cgi?kanji3a.sql を実行します。

kanji3a.sql

 # テーブル作成
 DROP TABLE IF EXISTS syain_sjis;
 CREATE TABLE syain_sjis (
   syain_no int(10) NOT NULL,
   syain_name varchar(50),
   bumon_no int(10),
   PRIMARY KEY (syain_no)
 )DEFAULT CHARACTER SET sjis;

CREATE TABLE のオプション(DEFAULT CHARACTER SET)を使用して文字セットをsjisに設定しています。データベース(test)の文字セットはutf8ですが、この方法を使えばサーバ側の文字セットをクライアント側に合わせる事ができます。

続いて、http://localhost/mysql/sqlfile.cgi?kanji3b.sql を実行します。

kanji3b.sql

SET NAMES sjis;
SHOW VARIABLES LIKE 'character\_set\_%';

# テーブルにデータを挿入
DELETE FROM syain;
INSERT INTO syain VALUES(1,'織田信長',3);
INSERT INTO syain VALUES(2,'豊臣秀吉',1);
INSERT INTO syain VALUES(3,'徳川家康',2);

# テーブルの検索
SELECT * FROM syain;

実行結果

perl-exp23.gif

上のSQLスクリプト(kanji3b.sql)や実行結果は、先のkanji1.sqlの場合とテーブル名を除いて同じですが、サーバ側(テーブル)の文字セットがクライアント側の文字セットと同じになっています。この場合、筆者の実験では、(株)などの機種依存文字も正しく表示できています。テーブルの文字セットがutf8の場合は上手く行きませんでした。

(株)はsjisコード:0x878AのWindows系機種依存文字です。



最終更新のRSS Last-modified: Sat, 13 Aug 2005 10:58:14 JST (4307d)