LoginSignup
0
3

More than 3 years have passed since last update.

MySQL8(多分それ以前のバージョンでも)にCSVデータをインポートするときのベストな方法

Posted at

前提

この記事は以下のような方にお勧めいたします。

  • DBの開発中で、MySQLサーバーとクライアントは同じマシン
  • MySQLでcsvファイルから大量のデータを入力したい
  • MySQL Workbenchからのインポートは遅すぎると苛立ちがちの方
  • "ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement"と表示されて実行できない方

実行環境

  • MySQL8.0.16
  • Windows10
  • サーバーとクライアントは同じマシン(テスト環境)

secure-file-privを正しく設定する

MySQLではサーバー上のローカルファイルからのインポートに、セキュリティの観点から制限をかけています。
これを回避して1ファイルからデータをインポートするためには、MySQLサーバーに入出力の権限のあるフォルダを指定してあげます。

my.iniに設定を追加する

設定の方法は単純です。サーバーへの設定ですのでmy.ini(またはmy.confなど)にアクセスを許可するフォルダを指定します。

my.ini
[mysqld]
#何かしらのその他の設定...
secure_file_priv=D:\import  #長いパス打ちたくないので浅い場所に専用フォルダを作った

上記の1行を追加するだけです。
既にサーバーを起動しているのであれば、設定は起動時に読み込まれるので再起動します。

設定の確認

サーバーにログインして、サーバーの変数を確認します。

mysql> SELECT @@GLOBAL.secure_file_priv;
+---------------------------------------------+
| @@GLOBAL.secure_file_priv                   |
+---------------------------------------------+
| #指定したパスが表示されるはずです              |
+---------------------------------------------+

上記が確認できたら、実際にコマンドを利用してインポートを行います。

コマンドラインからCSVをインポートする

LOAD DATA INFILE構文

MySQLでファイルからデータをインポートするにはLOAD DATA INFILE構文を使用します。公式のドキュメントによると、INSERTの20倍速いとのことです。

先に読み込むファイルの書式を確認しておきましょう。この記事では下記の書式を想定しています。

  • 文字コード:JIS(レガシーなデータを移行しようとしています)
  • データ形式はCSV(データの区切りはカンマ、文字列(データ)はダブルクォーテーションで括られています。)
  • 改行文字は\r\f(Windowsを想定しています)
  • ファイルの1行目はヘッダ行で実データではありません
  • テーブルの構造とファイルのデータの順は一致しています

LOAD DATA INFILE構文の利用例

上記のようなcsvファイルを読み込むのであれば、コマンドは下記のようになります。

load data infile 'D:\\import\\for-import.csv' #前述の例のようにsecure_file_privを指定したものとして
into table `new_table`     #インポート先のテーブル名
character set sjis         #ファイルの文字セット(JISSJISに含まれるためSJISを指定)
fields terminated by ','   #データフィールドの区切り文字(csvなのでカンマ)
enclosed by '"'            #データはダブルクォーテーションで括られている
lines terminated by '\r\n' #行末文字は\r\f
ignore 1 lines;            #1行目はヘッダなので読み込まない

パスを指定する際にリテラルとして扱うため、Windowsではバックスラッシュをエスケープする必要があります。
character setを指定することで、事前に文字セットを変換する必要がありません。ただし、ファイルに書き出された時点でテーブル内のすべてのカラムが一つの文字セットに統一されている必要があります。これは書き出しの時点の問題で、読み込み時には解決できないようです。
fields以降でフィールド(カラム)の解釈を指定します。terminated byでフィールドの区切り文字、enclosed byでデータを括っている文字を指定できます。また、escaped byでエスケープに使用される文字も指定できます。
同様に、lines以降で行に対する解釈を指定できます。terminated byで行の終わりを表す文字を指定します。Windowsでは通常、"\r\n"です。
ignore n linesで読み飛ばす行数を指定できます。

インポート先のテーブルと読み込むファイルのフィールド(カラム)の数が一致しているケースであれば、これでインポートが可能です。

Query OK, 30164 rows affected (4.07 sec)
Records: 30164  Deleted: 0  Skipped: 0  Warnings: 0

無事読み込みに成功したのであれば、上記のような結果になります。

まとめ

私はこれが、csvファイルをインポートするときのベストの手順だと思います。

補記

読み込み先のカラム、順序の指定

列とカラムの数が一致しなかったり、フィールドとカラムの順序が異なる場合は、別途、読み込み先のカラム名を読み込みの順序に合わせて指定する必要があります。
公式ドキュメントのカラムリストについてを参照してください。

読み込みの前処理

MySQL8では欠落したフィールドを埋めることができるように機能が追加されています。上記のカラムリストと合わせて使用します。

localと読み込みファイルの位置について

LOAD DATA INFILE構文をLOAD DATA LOCAL INFILEとして使用した場合、クライアントプログラムがファイルを読み込んでサーバーへ送信する、という挙動になります。
これは(サーバーとクライアントが同一マシンである)開発環境では無駄な動作ですが、リモートのサーバーにデータファイルをアップロードする場合には必要になります。
上記の例ではlocalを使用していないため、(たまたまクライアントマシンと同じマシン上で起動している)mysqlサーバーが直接、ファイルの読み込みを行っています。この時、セキュリティ対策として権限のある指定されたフォルダからのみファイルを読み込む設定が、secure_file_privとなります。これに空白を指定するような説明がされていることが多くありますが、それはバッドプラクティスです。
当然ながら、サーバーマシンのシェルにリモートクライアントからログインし、サーバー上のファイルを読み取る場合はlocalの指定は不要で、この記事のケースのようにsecure_file_privを指定しておく必要があります。その際も、もともとMySQLがアクセスできるフォルダを指定するのが安全だと思います。


  1. 回避という表現は適切ではないと思いますが 

0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3