見出し画像

【メモ】MySQL内のUTF-8のデータが文字化けしていたのを直す

はじめに

例の如くの備忘録です。
ある日、「MySQLのデフォルトの文字コードがlatin1になっていたのでUTF-8に変えたところ、Teampassからデータを見た時文字化けするようになってしまい、たぶんUTF-8でない文字コードでデータが保存されてると思うから原因を調べて欲しい」というお話がありました。
調べてみたところ、結果的にはTeampass側というよりはMySQL側の問題だったようです。
説明の都合上Teampassで利用しているテーブル名そのままの記述となっていますが、適宜自身の環境に合わせてDB名、テーブル名などを読み替えていただければと。
以下、調査手順。

手順

まあ何はともあれまずはDBを見に行きますか、ということでMySQL(MariaDB)のteampassデータベースにアクセスしてみます。

# mysql -u root -p  #mysqlに接続
MariaDB [(none)]> show databases;  #データベースの一覧を確認
MariaDB [(none)]> use teampass;  #teampassデータベースに接続
MariaDB [teampass]> show tables;  #テーブル一覧を確認
MariaDB [teampass]> select * from teampass_items\G  #各アイテムのレコードを確認

こうしてレコードを確認してみると、確かにlabelやdescriptionなどに『î』みたいな文字が入っているのが見えました。
ほうほうと思いつつ、もう少し詳しく調べてみます。

MariaDB [teampass]> show variables like '%char%';  #mysqlの文字コードを確認

+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       |
| character_set_connection | utf8                       |
| character_set_database   | utf8                       |
| character_set_filesystem | binary                     |
| character_set_results    | utf8                       |
| character_set_server     | utf8                       |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+

MySQL全体の文字コードは基本的にutf8になっている様子。
個別に文字コードがどうなっているか掘り下げます。

MariaDB [teampass]> show columns from teampass_items;  #データ型の確認

+---------------------------+--------------+------+-----+---------+----------------+
| Field                     | Type         | Null | Key | Default | Extra          |
+---------------------------+--------------+------+-----+---------+----------------+
| id                        | int(12)      | NO   | PRI | NULL    | auto_increment |
| label                     | varchar(500) | NO   |     | NULL    |                |
| description               | text         | YES  |     | NULL    |                |
| pw                        | text         | YES  |     | NULL    |                |
| pw_iv                     | text         | YES  |     | NULL    |                |
| pw_len                    | int(5)       | NO   |     | 0       |                |
| url                       | varchar(500) | YES  |     | NULL    |                |
| id_tree                   | varchar(10)  | YES  |     | NULL    |                |
| perso                     | tinyint(1)   | NO   |     | 0       |                |
| login                     | varchar(200) | YES  |     | NULL    |                |
| inactif                   | tinyint(1)   | NO   |     | 0       |                |
| restricted_to             | varchar(200) | YES  | MUL | NULL    |                |
| anyone_can_modify         | tinyint(1)   | NO   |     | 0       |                |
| email                     | varchar(100) | YES  |     | NULL    |                |
| notification              | varchar(250) | YES  |     | NULL    |                |
| viewed_no                 | int(12)      | NO   |     | 0       |                |
| complexity_level          | varchar(3)   | NO   |     | -1      |                |
| auto_update_pwd_frequency | tinyint(2)   | NO   |     | 0       |                |
| auto_update_pwd_next_date | varchar(100) | NO   |     | 0       |                |
| encryption_type           | varchar(20)  | NO   |     | not_set |                |
+---------------------------+--------------+------+-----+---------+----------------+
20 rows in set (0.003 sec)

MariaDB [teampass]> ALTER TABLE teampass_items MODIFY description text CHARACTER SET utf8 COLLATE utf8_general_ci;  #descriptionカラムをUTF8に文字コードを変更してみる
Query OK, 0 rows affected (0.029 sec)
Records: 0  Duplicates: 0  Warnings: 0

更新できたレコードは0件。つまりdescriptionはみんなUTF-8っぽいです。
さらに調べてみます。

MariaDB [teampass]> show create table teampass_items\G  #create tableステートメントを確認

*************************** 1. row ***************************
      Table: teampass_items
Create Table: CREATE TABLE `teampass_items` (
 `id` int(12) NOT NULL AUTO_INCREMENT,
 `label` varchar(500) NOT NULL,
 `description` text DEFAULT NULL,
 `pw` text DEFAULT NULL,
 `pw_iv` text DEFAULT NULL,
 `pw_len` int(5) NOT NULL DEFAULT 0,
 `url` varchar(500) DEFAULT NULL,
 `id_tree` varchar(10) DEFAULT NULL,
 `perso` tinyint(1) NOT NULL DEFAULT 0,
 `login` varchar(200) DEFAULT NULL,
 `inactif` tinyint(1) NOT NULL DEFAULT 0,
 `restricted_to` varchar(200) DEFAULT NULL,
 `anyone_can_modify` tinyint(1) NOT NULL DEFAULT 0,
 `email` varchar(100) DEFAULT NULL,
 `notification` varchar(250) DEFAULT NULL,
 `viewed_no` int(12) NOT NULL DEFAULT 0,
 `complexity_level` varchar(3) NOT NULL DEFAULT '-1',
 `auto_update_pwd_frequency` tinyint(2) NOT NULL DEFAULT 0,
 `auto_update_pwd_next_date` varchar(100) NOT NULL DEFAULT '0',
 `encryption_type` varchar(20) NOT NULL DEFAULT 'not_set',
 PRIMARY KEY (`id`),
 KEY `restricted_inactif_idx` (`restricted_to`,`inactif`)
) ENGINE=InnoDB AUTO_INCREMENT=1343 DEFAULT CHARSET=utf8
1 row in set (0.161 sec)

MariaDB [teampass]> select TABLE_SCHEMA,TABLE_NAME,COLUMN_NAME from information_schema.COLUMNS where COLLATION_NAME like 'utf8%';  #UTF8のカラムを確認

~~中略~~
| teampass           | teampass_items                                     | label                      |
| teampass           | teampass_items                                     | description                |
| teampass           | teampass_items                                     | pw                         |
| teampass           | teampass_items                                     | pw_iv                      |
| teampass           | teampass_items                                     | url                        |
| teampass           | teampass_items                                     | id_tree                    |
| teampass           | teampass_items                                     | login                      |
| teampass           | teampass_items                                     | restricted_to              |
| teampass           | teampass_items                                     | email                      |
| teampass           | teampass_items                                     | notification               |
| teampass           | teampass_items                                     | complexity_level           |
| teampass           | teampass_items                                     | auto_update_pwd_next_date  |
| teampass           | teampass_items                                     | encryption_type            |
~~中略~~

やはりteampass_itemsの各カラムの文字コードはUTF-8。
これは別のところに原因がありそうだな、ということで調べてみたところ、こちらの記事が見つかりました。
どうやらこちらの文字化けはUTF-8で二重にエンコードされることで生じる文字化けのようです。
こちらの現象はmysql上でレコードの文字コードを変更しても改善しないことが多いので、一旦元の文字コードでdumpを取り、そのdumpの文字コードをutf8に変えてインポートするのが良いと。ふむふむ。

その通りに対応してみます。
元の文字コードはlatin1なのでlatin1でdumpを取得し、UTF-8でインポートします。

#teampassデータベース中のteampass_itemsテーブルのdumpをlatin1の文字コードで取得
# mysqldump -u root -p --skip-set-charset --default-character-set=latin1 teampass teampass_items > convert-dump.sql

#上記dumpの文字コードをutf8にしてteampassデータベースにインポート
# mysql -u root -p --default-character-set=utf8 -D teampass < convert-dump.sql

これで文字化けが無事解消されました!

この記事が気に入ったらサポートをしてみませんか?