MySQL道普請便り

第41回 MySQLのCHAR型とVARCHAR型との違いを理解する

この記事を読むのに必要な時間:およそ 4.5 分

サービスを作成する時に,文字型を使わずに作成することはあまりないと思います。MySQLを使った場合でも,サービスを作成しようとした時には,やはり文字列型を使うことになります。その際に間違えやすいのがCHAR型とVARCHAR型です。2つの型は似た名前をしているのですが,あまり理解してないで使ってしまうと,手痛い目にあってしまうことも少なくありません。

そこで今回は,CHAR型とVARCHAR型に関して,実際にどのように違うのかを紹介していきたいと思います。

検証環境

今回使用した環境は,CentOS7.1上にyumコマンドを利用してインストールしたMySQL 5.7.17で行っています。

まず最初に,今回使う検証用のデータベースを以下のように作成しました。

mysql> CREATE DATABASE characters;
Query OK, 1 row affected (0.00 sec)
mysql> use characters
Database changed

また,サンプルとして使用しているテーブルは,全てlatin1の文字セットで確認しています。マルチバイト文字に関するお話は,最後にまとめて説明します。

CHAR型

CHAR型の特徴的な点は,値を格納された時に,もし文字列がテーブル作成時に指定された文字数よりも短かった場合,文字列の右側の末尾にスペースで補完します。そのため,この型の場合はデータは固定長で保存されます。たとえば以下のようなCREATE TABLE文を実行した場合,cカラムには4文字の長さの文字列を保存することができます。

mysql> CREATE TABLE ch(c CHAR(4)) CHARACTER SET latin1;
Query OK, 0 rows affected (0.03 sec)

また,ここで右側の末尾に付与されたスペースは,取り出す際に削除されて取り出されることを確認してみましょう。以下のように,4文字目に空白が入っているデータと空白を除去したデータをINSERTしました。

mysql> INSERT INTO ch(c) VALUES ('a b ');
Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO ch(c) VALUES ('a b');
Query OK, 1 row affected (0.01 sec)

続けて,chテーブルから先ほどINSERTしたデータをSELECTしたものが以下の結果になります。CONCATは文字列結合を行う関数で,今回空白が入っているかいないかがわかりにくいため使用しました。

mysql> SELECT CONCAT('(',c,')') FROM ch;
+-------------------+
| CONCAT('(',c,')') |
+-------------------+
| (a b)             |
| (a b)             |
+-------------------+
2 rows in set (0.00 sec)

上記の結果から,末尾に付いていたはずのスペースが除去されていることがわかります。どうしてこのような事になるかというと,末尾の足りない文字をスペースで埋めていることに起因しています。末尾にあるスペースがユーザのINSERTによるものなのか,足りない文字列を埋めるために追加されたものかどうかを判定する手段が無いため削られています。なので,末尾にスペースを保存する必要がある場合には使用できません。

CHAR型で指定できる長さ

CHAR型では,保存できる文字列の長さは0文字以上255文字以下の範囲で指定できます。

mysql> CREATE TABLE ch2(c CHAR(0)) CHARACTER SET latin1;
Query OK, 0 rows affected (0.01 sec)

mysql> CREATE TABLE ch3(c CHAR(255)) CHARACTER SET latin1;
Query OK, 0 rows affected (0.01 sec)

上のように,0文字から255文字の間であればテーブルを作成できますが,以下のように256文字以上を指定した場合には,BLOG型かTEXT型を代わりに使うようにというエラーが発生しています。

mysql> CREATE TABLE ch4(c CHAR(256)) CHARACTER SET latin1;
ERROR 1074 (42000): Column length too big for column 'c' (max = 255); use BLOB or TEXT instead

この型を使う際は,ハッシュ値などの桁数が揃った文字列を効率よく保管したい場合などが良いでしょう。

SQLモードの違い

今回の説明では,VARCHAR(4)に対して4文字までの文字をINSERTしていましたが,5文字以上の文字列をINSERTした場合どのような結果になるかを軽く説明していきたいと思います。今回使用したバージョンでは以下のようにエラーになります。しかし,利用したバージョンや設定によって結果が変わります。

原因は,バージョンによってSQLモードの暗黙のデフォルト値や設定ファイルにデフォルトの設定が違うためです。5.5系は暗黙のデフォルト値も設定ファイルのデフォルト値もstrictモードではないため,警告が出て切り詰めてINSERTされています。

5.6系からは暗黙のデフォルト値はstrictモードではありませんが,my.cnfファイル内にSQLモードが設定されているため,エラーになるように変更されました。

5.7からは暗黙のデフォルト値がstrictモードになっているため,設定ファイルには何も書かれていませんがエラーになります。また,5.5を使っている場合でも設定を行いたい場合はSTRICT_TRANS_TABLESSTRICT_ALL_TABLESをSQLモードに指定することで,無効な値をエラーにすることができます。設定が有効になっている場合は,以下のようにエラーが帰ってきます。

mysql> INSERT INTO ch(c) VALUES ('abcde');
ERROR 1406 (22001): Data too long for column 'c' at row 1

参考までに,5.5.54で実行した場合を以下に載せておきます。

mysql> INSERT INTO ch(c) VALUES ('abcde');
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> select c from ch;
+------+
| c    |
+------+
| abcd |
+------+
1 row in set (0.00 sec)

上記のように警告を出し,切り詰められてINSERTされていることがわかります。バージョンアップをする際には,暗黙のデフォルト値や設定のデフォルト値が変わる事に注意をしましょう。

著者プロフィール

木村浩一郎(きむらこういちろう)

株式会社オプティム 技術統括本部のエンジニア。最近はミドルウェア・インフラ周りのことも少しずつ学習しています。趣味は将棋。好きな戦法は四間飛車。

Twitter:@kk2170