※この記事にはアフィリエイト広告(プロモーション)が含まれます。
絵文字を含む文字列を入れると、次のエラーになることがあります。
ERROR 1366 (HY000): Incorrect string value: '\xF0\x9F\x8D\xA3' for column 's' at row 1
\xF0\x9F\x8D\xA3 は 🍣 の UTF-8 のバイト列です。\xF0 で始まる4バイトの文字を、3バイトまでしか扱えない utf8mb3 の列に入れようとしています。
この記事のログは MySQL 8.4.11(Ubuntu 26.04 の標準パッケージ)で採りました。8.4 のサーバー既定の文字コードは utf8mb4 なので、新しく作った表では起きません。古いバージョンから移した表や、CHARSET=utf8 を指定して作った表で起きます。
まず見る(結論)
- 列の文字コードを見る。
utf8mb3ならALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4 - 列が
utf8mb4なのに出るなら、クライアントの文字コード(SET NAMES、接続文字列)がutf8mb3になっていないかを見る
SELECT table_name, column_name, character_set_name, collation_name
FROM information_schema.columns WHERE table_schema = 'csdb';
+------------+-------------+--------------------+--------------------+
| TABLE_NAME | COLUMN_NAME | CHARACTER_SET_NAME | COLLATION_NAME |
+------------+-------------+--------------------+--------------------+
| t3b | s | utf8mb3 | utf8mb3_general_ci |
| t4 | s | utf8mb4 | utf8mb4_0900_ai_ci |
+------------+-------------+--------------------+--------------------+
utf8 と書くと utf8mb3 になる
8.4 でも、CHARACTER SET utf8 は utf8mb3 の別名です。表を作ると警告が出ます。
CREATE TABLE t3b (s varchar(20)) CHARACTER SET utf8;
SHOW WARNINGS;
| Warning | 3719 | 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. |
この列に日本語は入りましたが、絵文字は 1366 になりました。
非 strict モードでは ? に置き換わる
sql_mode に STRICT_TRANS_TABLES が無いと、エラーにならず、警告が出たうえで値が入ります。
SET sql_mode = '';
INSERT INTO t3b VALUES ('寿司🍣');
SHOW WARNINGS;
| Warning | 1366 | Incorrect string value: '\xF0\x9F\x8D\xA3' for column 's' at row 1 |
SELECT s, hex(s) FROM t3b;
| 寿司? | E5AFBFE58FB83F |
絵文字が ?(0x3F)に置き換わって保存されています。後で列を utf8mb4 に変えても、この ? は元に戻りません。8.4 の既定の sql_mode には STRICT_TRANS_TABLES が入っているので、アプリや設定ファイルで外していないかを確かめます。
クライアントの文字コードが原因のとき
列が utf8mb4 でも、接続の文字コードが utf8mb3 だと同じ 1366 になりました。
SET NAMES utf8mb3;
INSERT INTO t4 VALUES ('🍣');
ERROR 1366 (HY000): Incorrect string value: '\xF0\x9F\x8D\xA3' for column 's' at row 1
アプリの接続設定(JDBC の characterEncoding、PHP の charset など)で utf8 を指定している場合は、utf8mb4 に変えます。
接続の文字コードが latin1 の場合は、エラーにも警告にもならずに文字化けして入りました。
SET NAMES latin1;
INSERT INTO t4 VALUES ('日本');
SET NAMES utf8mb4;
SELECT s, hex(s) FROM t4;
| 日本 | C3A6E28094C2A5C3A6C593C2AC |
日本(E697A5E69CAC)の各バイトを latin1 の1文字とみなして変換したため、別の文字列として保存されています。--default-character-set=latin1 で接続し直すと 日本 と表示されるので、表示だけでは気づけません。hex() で確かめます。
utf8mb4 に変換する
ALTER TABLE t3b CONVERT TO CHARACTER SET utf8mb4;
表の既定値と、文字列の列すべてが utf8mb4(照合順序は utf8mb4_0900_ai_ci)になり、絵文字が入るようになりました。表を作り直すので、大きい表では時間がかかります。
ALTER DATABASE csdb CHARACTER SET utf8mb4;
SHOW CREATE TABLE t5\G
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3
ALTER DATABASE が効くのは、この後に作る表だけです。既存の表は1つずつ CONVERT TO します。
索引の長さの上限
utf8mb4 は1文字を4バイトで見積もるので、索引の長さの上限(3,072 バイト)に早く届きます。
CREATE TABLE t6 (s varchar(1000) CHARACTER SET utf8mb4, KEY(s));
ERROR 1071 (42000): Specified key was too long; max key length is 3072 bytes
varchar(768)(768 × 4 = 3,072 バイト)なら作れました。