MySQLではcharacter set
charsetやcollationとはなにかについては説明はしません。よって、
charsetやcollationの各レベルの設定方法
グローバル
以下のシステム変数を設定します。
character_… サーバーのデフォルトのcharsetset_ server collation_… サーバーのデフォルトのcollationserver
データベース
CREATE DATABASE文に指定することができます。
CREATE DATABASE `test` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
上記例は、testデータベースのcharsetにutf8mb4、utf8mb4_を設定しています。
テーブルとカラム
CREATE TABLE文に指定します。
CREATE TABLE tbl1 (
id bigint NOT NULL,
col1 varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_ci NOT NULL
)
ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
上記例は、tbl1テーブルのcharsetにutf8、utf8_を設定しています。またtbl1.カラムのcharsetにutf8mb4、utf8mb4_を設定しています。
MySQLのデフォルトcollationの注意点
では、
CREATE DATABASEやCREATE TABLE文ではDEFAULT CHARSET=xx COLLATE=xxの部分を省略することもできます。CREATE TABLE文を例に、
前提として、
- グローバルレベルのcharset… utf8mb4
- グローバルレベルのcollation…
utf8mb4_bin
mysql> show global variables like 'collation_server'; +------------------+-------------+ | Variable_name | Value | +------------------+-------------+ | collation_server | utf8mb4_bin | +------------------+-------------+
- データベースレベルのcharset… utf8mb4
- データベースレベルのcollation…
utf8mb4_unicode_ ci
mysql> CREATE DATABASE `db` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; 1 row in set (0.00 sec)
dbデータベース内に以下の3つタイプのCREATE TABLEを実行すると、
CREATE TABLE t1 (col1 varchar(10));CREATE TABLE t2 (col1 varchar(10)) DEFAULT CHARSET=utf8mb4;CREATE TABLE t3 (col1 varchar(10)) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ ci;
結果は以下の通りです。
| Table | collation |
|---|---|
| t1 | utf8mb4_ |
| t2 | utf8mb4_ |
| t3 | utf8mb4_ |
t1については、utf8mb4_という、
つまり、DEFAULT CHARSET=utf8mb4のみ記述すると、collation_システム変数に設定されているサーバーのデフォルトcollationでもデータベースレベルのcollationでもない、DEFAULT CHARSET=xx COLLATE=xxを省略せずに記述するのが良いでしょう。
また、SHOW COLLATION文を実行します。DefaultカラムがYesになっているものが、
ちなみに、CREATE DATABASE文についても同様の動きをします。DEFAULT CHARACTER SET xx COLLATE xxを省略すると、
CREATE DATABASE db1 ;CREATE DATABASE db2 DEFAULT CHARACTER SET utf8mb4;CREATE DATABASE db3 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ ci;
結果は以下の通りです。
| Database | collation |
|---|---|
| db1 | utf8mb4_ |
| db2 | utf8mb4_ |
| db3 | utf8mb4_ |
MySQL 8.0アップグレード時の注意点
前述のデフォルトcollationはMySQL 8.utf8mb4_であり、utf8mb4_に変更されました。
MySQL 5.utf8mb4_で運用されているMySQL 5.
# mysqldump --no-data test57db test_account -p -- MySQL dump 10.13 Distrib 5.7.22, for linux-glibc2.12 (x86_64) <snip> CREATE TABLE `test_account` ( `user_id` bigint(20) NOT NULL, `created_at` int(10) unsigned NOT NULL, `updated_at` int(10) unsigned NOT NULL, PRIMARY KEY (`user_id`), ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; <snip> -- Dump completed on 2021-10-07 22:18:18
DEFAULT CHARSET=utf8mb4のみの記述で出力されてしまいます。このdumpファイルをそのままMySQL8.utf8mb4_ではなくutf8mb4_が設定されてしまいます。
この問題を解決するためには、default_システム変数を使用します。このシステム変数はutf8mb4におけるMySQLのデフォルトcollationを指定します。デフォルトはutf8mb4_です。これをutf8mb4_に変更します。
mysql> SET GLOBAL default_collation_for_utf8mb4=utf8mb4_general_ci;
この変更をすると、DEFAULT CHARSET=utf8mb4のみの記述のCREATE TABLE文であっても、utf8mb4_でテーブルが作成されます。
default_システム変数の注意点として、SET PERSISTを使って設定します。詳しくは第94回 SET PERSISTを使ってシステム変数を永続化させるをご参照ください。また、
テーブル間でcollationが異なるときの問題
テーブル間でcollationが異なるときに起こる問題について紹介したいと思います。その場合、
たとえば、
CREATE TABLE `t1` ( `id` int NOT NULL, `col1` varchar(10) COLLATE utf8mb4_bin DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; CREATE TABLE `t2` ( `id` int NOT NULL, `col1` varchar(10) COLLATE utf8mb4_general_ci DEFAULT NULL, PRIMARY KEY (`id`), KEY(col1) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; INSERT INTO t1 VALUES (1,'a'),(2,'b'),(3,'b'); INSERT INTO t2 VALUES (1,'a'),(2,'b'),(3,'b');
t1.がutf8mb4_でt2.がutf8mb4_とします。このときに駆動表にt1をt2のcol1と結合するクエリを実行してみます。
mysql> explain SELECT * FROM t1 JOIN t2 ON t1.col1=t2.col1; +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------------------------+ | 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | NULL | | 1 | SIMPLE | t2 | NULL | ALL | col1 | NULL | NULL | NULL | 3 | 33.33 | Range checked for each record (index map: 0x2) | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------------------------+
本来であれば、t2.のインデックスを効かせて解決できるはずですが、
Tipsとして、t1 → t2の順で結合するように指定しています。
mysql> explain SELECT /*+ JOIN_ORDER(t1,t2) */ * FROM t1 > JOIN t2 ON t1.col1 COLLATE utf8mb4_general_ci=t2.col1; +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------+ | 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | NULL | | 1 | SIMPLE | t2 | NULL | ref | col1 | col1 | 43 | func | 1 | 100.00 | Using where; Using index | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------+
しかし、