MySQLに日本語でINSERTできない場合

MySQLは幾度もインストールしているが、毎回文字コードの設定では悩まされる。忘れてしまっていることもあるが、本質的に理解できていないので、毎回文字化けなどを起こしてしまうのだ。昨日も作成したデータベースに日本語をINSERTしようとすると以下のようなエラーが出た。対応のメモを残しておく。


Incorrect string value: '\xE3\x81\x95\xE3\x81\x82...'
for column 'NAME' at row 1 at com.mysql.jdbc
単に以下のようなINSERT文をJavaからexecuteUpdate()で実行しただけだ。同じことをMySQLコンソールからやった場合にはうまくいった。

$ mysql -uroot -p***
> use msql
> create database test;
> flush privileges;
> use test
> create table DUMMY (
ID varchar(10) primary key,
NAME varchar(20) not null
);
> insert into DUMMY (ID, NAME) values ('001', 'たこはち');
以下のように対処した。まず、MySQLコンソールからcharacter_setをチェックしてみた。

$ mysql -uroot -p***
> use mysql
> show variables like 'char%';
+--------------------------+--------+
| character_set_client | latin1 |
| character_set_connection | latin1 |
| character_set_database | latin1 |
| character_set_filesystem | binary |
| character_set_results | latin1 |
| character_set_server | latin1 |
| character_set_system | utf8 |
+--------------------------+--------+
character_set_databaseなどがlatin1になっているのでこれをsjisにする必要がある。MySQLのインストール時にデフォルトのままだとlatin1(もしくはutf8)になる。スタートメニュー経由などでMySQL Server Instance Configuration Wizardを起動して、default character setの設定画面まで進める。このWizardは、以前、「MySQL 5.1.37のインストールで失敗」でも書いたように、何度も実行したことのあるMySQLInstanceConfig.exeだ。そこでデフォルトでlatin1になっているのをsjisに変更する。
また、MySQLのインストール先にあるmy.iniファイルを開いてみて、default-character-set=latin1(もしくはutf8)となっていたら、それもsjisに変更する。Wizardを実行するとmy.iniファイルが更新されるが、必ずしもdefault-character-setは自動変更されないようなので、手作業で修正しておく。

[mysql]
default-character-set=sjis

[mysqld]
default-character-set=sjis

MySQLサービスを再起動後に、再度、MySQLコンソールからcharacter_setをチェックしてみる。

$ mysql -uroot -p***
> use mysql
> show variables like 'char%';
+--------------------------+--------+
| character_set_client | sjis |
| character_set_connection | sjis |
| character_set_database | sjis |
| character_set_filesystem | binary |
| character_set_results | sjis |
| character_set_server | sjis |
| character_set_system | utf8 |
+--------------------------+--------+
しかし、既存の作成済みのデータベースのcharacter_set_databaseは変わらないことに注意する必要がある。これはデータベースをdropして再作成するか、setコマンドで変更してやる必要がある。alter database文で文字コードを指定して変更することも、勿論可能だ。

> use test
> show variables like 'char%';
+--------------------------+--------+
| character_set_client | sjis |
| character_set_connection | sjis |
| character_set_database | latin1 |
| character_set_filesystem | binary |
| character_set_results | sjis |
| character_set_server | sjis |
| character_set_system | utf8 |
+--------------------------+--------+
> set character_set_database = sjis;
> show variables like 'char%';
+--------------------------+--------+
| character_set_client | sjis |
| character_set_connection | sjis |
| character_set_database | sjis |
| character_set_filesystem | binary |
| character_set_results | sjis |
| character_set_server | sjis |
| character_set_system | utf8 |
+--------------------------+--------+
setコマンドでの変更も一時的には有効だ。しかし恒久的なものではなく、MySQLサービスが再起動されると元に戻ってしまう。以前には、サービス起動の引数でcharacter_setを指定したこともあるが、今回はやめておく。一番いいのは、作成済みのデータベースを一旦drop databaseして新たにcreate databaseする方法だろう。まだ実データがない開発段階では、それが望ましい。新規に作成したデータベースのcharacter_set_databaseにはsjisが適用されるようになる。

> use test
> show variables like 'char%';
+--------------------------+--------+
| character_set_client | sjis |
| character_set_connection | sjis |
| character_set_database | latin1 |
| character_set_filesystem | binary |
| character_set_results | sjis |
| character_set_server | sjis |
| character_set_system | utf8 |
+--------------------------+--------+
> drop database test;
> create database test;
> use test
> show variables like 'char%';
+--------------------------+--------+
| character_set_client | sjis |
| character_set_connection | sjis |
| character_set_database | sjis |
| character_set_filesystem | binary |
| character_set_results | sjis |
| character_set_server | sjis |
| character_set_system | utf8 |
+--------------------------+--------+
ちなみにJavaMySQLからデータベースに接続するときは、勿論、以下の感じでcharacterEncodingにSJISを指定しておくこと。

try {
DbUtils.loadDriver("com.mysql.jdbc.Driver");
} catch (Exception ex) {
;
}
// String url = "jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=SJIS";
String url = "jdbc:mysql://localhost/" + "test"; // database名がtestの場合
Properties props = new Properties();
props.put("user", "root"); // 任意
props.put("password", "***"); // 任意
props.put("useUnicode", "true"); // これが必要
props.put("characterEncoding", "SJIS"); // これが必要
try {
this.conn = DriverManager.getConnection(url, props);
} catch (SQLException ex){
;
}
[2010/02/02追記]
文字コード関連の変数についての情報があったので追記しておく。文字コードはcreate database文やalter database文でが指定でき、databaseごとに使い分けが行える。character_set_database, character_set_client, character_set_connection, character_set_resultsは同じ文字コードに設定しておくことが無難そうだ。
変数内容
character_set_systemシステムがテーブル名やカラム名などの登録する際に使用する文字コード。utf8で固定されている。
character_set_servercharacter_set_databaseのデフォルト値となる文字コード
character_set_databasecreate database文で文字コードが指定されなかった場合、作成されたdatabaseの文字コードはこれになる。character_set_connectionのデフォルト値になる。
character_set_clientサーバがクライアントから受け取るSQL文の文字コード
character_set_connectionクライアントから受け取ったSQL文をサーバが、この文字コードに変換して取り込む。通常は、character_set_client,character_set_connection,character_set_resultsは同じ文字コードにする。
character_set_resultsサーバがクライアントに返す結果の文字コード。この変数をnullに設定すると、結果に対する文字コード変換は行われない。