【MySQL】JSON型を含むテーブルをCSVエクスポートして別MySQLにインポート(sedで加工)
目次
JSON 型を含むテーブルを CSV エクスポートする方法と MySQL インポートする方法を調べました。
RDBでJSON型なんてまず扱わないかもしれないけど、私はまれに使います。こういうちょっとした小技は忘れがちで
CSVエクスポートほうやり方忘れちゃったから、ネットでさっと調べてコピペしちゃおう。あ、 JSON型のせいで「”」が使えない、、、
となってしまったりして、どうでもいいところで時間食ったりとか。次からは脳死でもできるように自分なりの方法をまとめておこうと思います。
作業はMacでやってます。
MySQL5.7.12と8.0.24で動作を確認しました。
MySQLのJSON型を含むテーブルをCSVエクスポート
実際のコマンド
$ mysql -h ホスト名 -u ユーザー名 -p DB名 -e "select * from テーブル;” | sed -e "s/^/'/g" | sed -e "s/\$/'/g" | sed -e "s/\t/','/g" | sed "s/'NULL'/NULL/g" > ./export.csv
例)samplesというJSON型を含むテーブルがあったとして
$ mysql -h H -u U -p DBNAME -e "select * from samples;" > export.csv
$ cat export.csv
id temp_max temp_min coord weather icon
1 14.51 12.96 {"lat": 34.8537, "lon": 135.638} [{"id": 803, "main": "Clouds"}] NULL
上記のデータがsedによって加工されて以下のようになる。
$ mysql -h H -u U -p DBNAME -e "select * from samples;" | sed -e "s/^/'/g" | sed -e "s/\$/'/g" | sed -e "s/\t/','/g" | sed "s/'NULL'/NULL/g" > export.csv
$ cat export.csv
'id','temp_max','temp_min','coord','weather','icon'
'1','14.51','12.96','{"lat": 34.8537, "lon": 135.638}','[{"id": 803, "main": "Clouds"}]',NULL
補足:処理の流れ
処理前の状態
id temp_max temp_min coord weather icon
1 14.51 12.96 {"lat": 34.8537, "lon": 135.638} [{"id": 803, "main": "Clouds"}] NULL
先頭に「'」をつける
$ mysql -h H -u U -p DBNAME -e "select * from samples;" | sed -e "s/^/'/g" > export.csv
実行結果
id temp_max temp_min coord weather icon
1 14.51 12.96 {"lat": 34.8537, "lon": 135.638} [{"id": 803, "main": "Clouds"}] NULL
末尾に「'」をつける
$ mysql -h H -u U -p DBNAME -e "select * from samples;" | sed -e "s/^/'/g" | sed -e "s/\$/'/g" > export.csv
実行結果
'id temp_max temp_min coord weather icon'
'1 14.51 12.96 {"lat": 34.8537, "lon": 135.638} [{"id": 803, "main": "Clouds"}] NULL'
タブ「\t」を「','」に置き換える
$ mysql -h H -u U -p DBNAME -e "select * from samples;" | sed -e "s/^/'/g" | sed -e "s/\$/'/g" | sed -e "s/\t/','/g" > export.csv
実行結果
'id','temp_max','temp_min','coord','weather','icon' '1','14.51','12.96','{"lat": 34.8537, "lon": 135.638}','[{"id": 803, "main": "Clouds"}]','NULL'
「'NULL'」を「NULL」に置き換える
$ mysql -h H -u U -p DBNAME -e "select * from samples;" | sed -e "s/^/'/g" | sed -e "s/\$/'/g" | sed -e "s/\t/','/g" | sed "s/'NULL'/NULL/g" > export.csv
実行結果
'id','temp_max','temp_min','coord','weather','icon' '1','14.51','12.96','{"lat": 34.8537, "lon": 135.638}','[{"id": 803, "main": "Clouds"}]',NULL
MySQLにCSVインポート
local_infile=1の確認
MySQLにログインし
show variables Variable_name = 'local_infile';
ONかどうか確認する。必要に応じて値を変更する。
set global local_infile = 1;
AWS RDSの場合は、パラメータグループからlocal_infileを検索して1に変更する。

load data local infile でインポート
オプション付きでMySQLにログインし、ローカルファイルをインポートするSQLを実行する。ファイルパスは相対でも絶対でも大丈夫。
mysql -h ホスト名 -u ユーザー名 -p DB名 --local_infile=1
mysql> load data local infile "ファイルのパス" into table テーブル名 FIELDS TERMINATED BY ',' ENCLOSED BY "'" IGNORE 1 LINES;
詳細は公式リファレンスを参照。
バージョン5.6のものだが、2021/10/27時点では5.7.12と8.0.23で動作確認できた。
NULL値の扱い
公式から、NULL値について書かれた箇所を抜粋すると
FIELDS ENCLOSED BY が空でない場合、リテラルワード NULL をその値として含むフィールドは NULL 値として読み取られます。これは、文字列 'NULL' として読み取られる、FIELDS ENCLOSED BY 文字で囲まれたワード NULL とは異なります。
なので、上記の文法だったらNULLとすることでNULL値として処理してくれる。
データが重複した場合
データが重複したり、外部キー制約に違反したなど、何かしらの理由でデータがインポートできない場合、そのデータは無視される。以下のような感じ。
mysql> load data local infile ......
Query OK, 0 rows affected, 5 warnings (0.02 sec)
Records: 5 Deleted: 0 Skipped: 5 Warnings: 5
この場合、インポートしようとしたRecordsは5行あったが、そのうち5行に警告が出て、5行スキップされた。つまり全て無視された。
警告を見る場合は以下を実行する。
show warnings;
まとめ
CSVにエクスポート
mysql -h H -u U -p DBNAME -e "select * from samples;" | sed -e "s/^/'/g" | sed -e "s/\$/'/g" | sed -e "s/\t/','/g" | sed "s/'NULL'/NULL/g" > export.csv
オプション付きでMySQLにログイン
mysql -h ホスト名 -u ユーザー名 -p DB名 --local_infile=1
load data local infile を実行
load data local infile "ファイルのパス" into table テーブル名 FIELDS TERMINATED BY ',' ENCLOSED BY "'" IGNORE 1 LINES;