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

HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

文字列型とマルチバイト文字

MySQLで、何も考えずに「VARCHAR(n)」な列に日本語n文字を格納しようとしたら格納できてしまい、DB2での記憶と違うということで一通り調べてみたらしい。

作ったテーブルは次のとおり。

CREATE TABLE tbl1(str CHAR(6) NOT NULL)
CREATE TABLE tbl2(str NCHAR(6) NOT NULL)
CREATE TABLE tbl3(str VARCHAR(6) NOT NULL)
CREATE TABLE tbl4(str NVARCHAR(6) NOT NULL)
CREATE TABLE tbl5(str NVARCHAR2(6) NOT NULL)

最後のは完全にOracle用。「VARCHAR」はOracleでも互換性のためにVARCHAR2のシノニムとして残してあるということだったので、あえてVARCHAR2と明示したものは試さない。
それぞれのテーブルに対して実行するクエリは次のとおり。

INSERT INTO tbl1(str) VALUES('a')
UPDATE tbl1 SET str = 'あ'
UPDATE tbl1 SET str = 'あい'
UPDATE tbl1 SET str = 'あいう'
UPDATE tbl1 SET str = 'あいうえ'
UPDATE tbl1 SET str = 'あいうえお'
UPDATE tbl1 SET str = 'あいうえおか'
UPDATE tbl1 SET str = 'あいうえおかき'

トランザクションなしで上記クエリを実行するので、実行後にテーブルの中身を見てみれば、どこまでの長さの文字列を格納できるのかが分かる。
ちなみに、これらのクエリはJavaプログラムからの実行で、実際には以下のように生成する。(ソースコードに直に"あいうえおかき"と書いているわけじゃないからソースコードの文字エンコーディングは関係ないよ、と言いたいだけらしい(謎))

String sql = "UPDATE tbl1 SET str = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, "\u3042\u3044\u3046\u3048\u304a\u304b\u304d");

とりあえず今ある環境をそのまま使ったため、データベース(やテーブル)の文字エンコーディングUTF-8だったりShift_JISだったりするので、どうやってまとめようか悩んだが‥
結果は次のような感じ。「B」はバイト数、「C」は文字数を、n(CHAR(n)/NCHAR(n)/VARCHAR(n)/NVARCHAR(n)/NVARCHAR2(n))が表していることを示す。「×」はそのデータ型がサポートされていないことを示す。また、「*」はnの指定に関係なく何文字でも入りそうであることを示す。

DBMS CHAR NCHAR VARCHAR NVARCHAR NVARCHAR2
DB2 9.1 × × ×
DB2 9.5 × × ×
DB2 9.7 ×
Oracle 10g ×
PostgreSQL 8.0 × ×
PostgreSQL 8.1 × ×
PostgreSQL 8.2 × ×
PostgreSQL 8.3 × ×
PostgreSQL 8.4 × ×
PostgreSQL 9.0 × ×
MySQL 4.1 ×
MySQL 5.0 ×
MySQL 5.1 ×
MySQL 5.5 ×
Firebird 2.1 × ×
Firebird 2.5 × ×
Derby 10.5.3.0 × × ×
SQLite 3.6.14.2
SQLServer 2005 ×
SQLServer 2008 ×
H2 1.2
H2 1.3
HSQLDB 1.8.1 × × ×

こうしてみると、Firebirdと古いバージョンのDB2以外は、何らかの方法でnが文字数であるという定義ができることが分かる。
ただ、DB2をメインで使っていた当時(何時)は、DB2 9.1/9.5だったので、『UTF-8は最大で「文字数×4」バイトが必要だから‥』という定義をしていた記憶がよみがえったらしい(謎)。