MySQLでCSVファイルを入出力する完全ガイド【MySQL 8.x対応】

2011年5月11日


MySQLでデータをCSV形式で入出力したいケースは多い。バックアップ・データ移行・Excelとのデータ連携・分析ツールへの受け渡しなど、CSVはあらゆる場面で使われるフォーマットだ。

このガイドでは以下の内容を実際のコマンド・設定例を交えて解説する。

  • SELECT INTO OUTFILE でテーブルデータをCSVにエクスポートする方法
  • LOAD DATA INFILE でCSVファイルをMySQLにインポートする方法
  • MySQL 8.xで必要なセキュリティ設定(local_infilesecure_file_priv
  • 文字コード・改行コードの落とし穴と対処法
  • mysqldumpやmysqlimportとの使い分け

実際にMySQL 8.0.36環境で動作確認済みの手順を掲載している。

MySQLのCSVエクスポートとインポートの全体フロー図


前提条件

このガイドを進める前に、以下の環境・知識が必要です。

  • 環境: Linux / macOS / Windows(WSL2)
  • MySQLバージョン: MySQL 5.7以上(MySQL 8.x推奨)
  • 必要な権限: FILE権限(OUTFILE/INFILE使用時)
# バージョン確認
mysql --version
# 例: mysql  Ver 8.0.36 Distributed by The Debian Project, for Debian GNU/Linux 12 on x86_64

# MySQLに接続して権限確認
mysql -u your_user -p -e "SHOW GRANTS FOR CURRENT_USER();"

全体の流れ

このガイドは以下のステップで構成されている。

  1. エクスポート: SELECT INTO OUTFILE — 5分
  2. インポート: LOAD DATA INFILE — 5分
  3. MySQL 8.xのセキュリティ設定 — 10分
  4. 文字コード・改行コードの対処 — 5分
  5. 応用編: mysqldump・mysqlimportとの使い分け

ステップ1: CSVエクスポート(SELECT INTO OUTFILE)

SELECT INTO OUTFILE はSELECT文の結果をファイルに直接書き出すMySQL独自の構文だ。SQL文で条件・ソート・結合が自由に使えるため、柔軟なデータ抽出が可能になる。

基本構文

SELECT
  id,
  name,
  email,
  created_at
FROM
  users
WHERE
  created_at >= '2024-01-01'
ORDER BY
  id
INTO OUTFILE '/var/lib/mysql-files/users_export.csv'
CHARACTER SET utf8mb4
FIELDS
  TERMINATED BY ','
  ENCLOSED BY '"'
  ESCAPED BY '\\'
LINES
  TERMINATED BY '\n';

INTO OUTFILE はSELECT文の末尾に記述する。ファイルパスはMySQLサーバ上のパスになることに注意(クライアントのローカルパスではない)。

FIELDSオプションの詳細

オプション 説明 推奨値
TERMINATED BY 列の区切り文字 , (CSV)または \t(TSV)
ENCLOSED BY 列を囲む文字 " (ダブルクォート)
ESCAPED BY エスケープ文字 \\ (バックスラッシュ)

ENCLOSED BY '"' を指定すると、フィールド値にカンマや改行が含まれていても正しくエスケープされる。データに任意のテキストを含む可能性がある場合は必ず指定すること。

LINESオプションの詳細

オプション 説明 値の選択
TERMINATED BY 行末文字 \n(Linux/Mac)または \r\n(Windows向け)
STARTING BY 各行の先頭に挿入する文字列 通常は指定不要

Windowsで開くCSVを出力する場合は TERMINATED BY '\r\n' にするとExcelでの文字化けを防ぎやすい。

ヘッダー行を付けたい場合

SELECT INTO OUTFILE はヘッダー行を自動出力しない。ヘッダーを付けるにはUNIONを使う。

-- ヘッダー行 + データ行をまとめてCSV出力
SELECT 'id', 'name', 'email', 'created_at'
UNION ALL
SELECT
  id,
  name,
  email,
  created_at
FROM users
INTO OUTFILE '/var/lib/mysql-files/users_with_header.csv'
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n';

SELECT INTO OUTFILEコマンドの実行結果(ターミナル)

出力ファイルのパーミッションに注意

出力されたファイルのオーナーはMySQLサーバプロセスのユーザ(通常はmysqlユーザ)になる。一般ユーザで読み取るには権限変更が必要な場合がある。

# ファイルオーナー確認
ls -la /var/lib/mysql-files/users_export.csv
# -rw-rw-rw- 1 mysql mysql 2048 Mar  2 10:00 users_export.csv

# 必要に応じて権限変更
sudo chmod 644 /var/lib/mysql-files/users_export.csv

ステップ1完了の確認: /var/lib/mysql-files/ にCSVファイルが生成されていること。wc -l でレコード数がテーブルの行数と一致することを確認。


ステップ2: CSVインポート(LOAD DATA INFILE)

LOAD DATA INFILE はCSVファイルをMySQLテーブルに一括インポートするSQL文だ。INSERT を1行ずつ実行するより圧倒的に高速で、数百万行のデータも短時間で取り込める。

基本構文

LOAD DATA INFILE '/var/lib/mysql-files/users_import.csv'
INTO TABLE users
CHARACTER SET utf8mb4
FIELDS
  TERMINATED BY ','
  ENCLOSED BY '"'
  ESCAPED BY '\\'
LINES
  TERMINATED BY '\n'
IGNORE 1 LINES  -- ヘッダー行をスキップ
(id, name, email, created_at);

IGNORE 1 LINES でCSVの1行目(ヘッダー行)をスキップできる。カラムリストを最後に明示することで、CSVの列順とテーブルのカラム順が異なる場合にも対応できる。

REPLACE / IGNORE オプション

既存データとのPKやユニーク制約の衝突をどう処理するかを指定できる。

-- 重複行を置き換える(DELETE + INSERT相当)
LOAD DATA INFILE '/var/lib/mysql-files/users_import.csv'
REPLACE
INTO TABLE users
...

-- 重複行はスキップする(エラーを無視)
LOAD DATA INFILE '/var/lib/mysql-files/users_import.csv'
IGNORE
INTO TABLE users
...

REPLACE はUPDATEではなくDELETE + INSERTとして動作する点に注意。外部キー制約がある場合は予期しない削除が発生することがある。

列変換・SET句の使い方

CSVの値を加工してからインポートしたい場合は SET 句を使う。

LOAD DATA INFILE '/var/lib/mysql-files/users_import.csv'
INTO TABLE users
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 LINES
(@id, @name, @email, @created_at)
SET
  id = @id,
  name = TRIM(@name),             -- 空白除去
  email = LOWER(@email),          -- 小文字統一
  created_at = STR_TO_DATE(@created_at, '%Y/%m/%d %H:%i:%s');  -- 日付フォーマット変換

@変数名 でCSVの列を一時変数として受け取り、SET 句で変換しながらカラムに代入できる。

LOAD DATA INFILEコマンドの実行結果(MySQLクライアント)

ステップ2完了の確認: SELECT COUNT(*) FROM users; でインポートしたレコード数がCSVの行数と一致すること。


ステップ3: MySQL 8.xのセキュリティ設定

MySQL 8.xではLOAD DATA INFILESELECT INTO OUTFILEのセキュリティが強化されており、デフォルト設定のままでは動作しないケースがある。ここで必要な設定を確認する。

secure_file_priv の設定

secure_file_priv はファイル入出力を許可するディレクトリを制限するシステム変数だ。

-- 現在の設定を確認
SHOW VARIABLES LIKE 'secure_file_priv';

-- 例: 出力
-- +------------------+-----------------------+
-- | Variable_name    | Value                 |
-- +------------------+-----------------------+
-- | secure_file_priv | /var/lib/mysql-files/ |
-- +------------------+-----------------------+

secure_file_priv の値によって動作が変わる。

動作
/path/to/dir/ 指定ディレクトリのみファイルアクセス可能
“ (空文字) すべてのディレクトリにアクセス可能(非推奨)
NULL ファイルアクセスを完全に無効化

デフォルトは /var/lib/mysql-files/(Linuxの場合)。INTO OUTFILE / LOAD DATA INFILE で使うパスはこのディレクトリ配下にする必要がある。

secure_file_priv を変更するには my.cnf(または my.ini)を編集してMySQLを再起動する必要がある(動的変更不可)。

# /etc/mysql/mysql.conf.d/mysqld.cnf(Ubuntu/Debianの場合)
[mysqld]
secure_file_priv = /var/lib/mysql-files/

LOAD DATA LOCAL INFILE の設定

クライアントからのローカルファイルをインポートする LOAD DATA LOCAL INFILE は、MySQL 8.0からデフォルトで無効になっている。

-- サーバ側の設定確認
SHOW VARIABLES LIKE 'local_infile';
-- +---------------+-------+
-- | Variable_name | Value |
-- +---------------+-------+
-- | local_infile  | OFF   |
-- +---------------+-------+

有効化するにはサーバ設定とクライアント接続の両方で設定が必要だ。

# my.cnf でサーバ側を有効化
[mysqld]
local_infile = ON
# クライアント接続時に有効化
mysql --local-infile=1 -u your_user -p database_name
-- SQL内でもLOCALを明示
LOAD DATA LOCAL INFILE '/path/on/client/users.csv'
INTO TABLE users
...

セキュリティ注意: LOCAL INFILE を有効にすると、悪意あるサーバが接続してきたクライアントのファイルを読み取れるリスクがある。信頼できるMySQLサーバにのみ接続する場合に限り有効化すること。本番環境では LOAD DATA INFILE(サーバ側ファイル)の使用を推奨する。

FILE権限の付与

SELECT INTO OUTFILELOAD DATA INFILE の使用には FILE グローバル権限が必要だ。

-- FILE権限を付与
GRANT FILE ON *.* TO 'your_user'@'localhost';
FLUSH PRIVILEGES;

-- 権限確認
SHOW GRANTS FOR 'your_user'@'localhost';

FILE 権限はグローバル権限のため ON *.* で指定する必要がある。データベース単位での権限付与はできない。

MySQLのセキュリティ設定フロー(secure_file_priv・FILE権限)

ステップ3完了の確認: SHOW VARIABLES LIKE 'secure_file_priv'; の出力が期待通りのパスを示し、対象ユーザにFILE権限が付与されていること。


ステップ4: 文字コード・改行コードの対処

CSVの入出力で最もよくトラブルになるのが文字コードと改行コードだ。

文字コードの指定

CHARACTER SET 句で明示的に文字コードを指定する。

-- エクスポート時
SELECT * FROM articles
INTO OUTFILE '/var/lib/mysql-files/articles.csv'
CHARACTER SET utf8mb4
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n';

-- インポート時
LOAD DATA INFILE '/var/lib/mysql-files/articles.csv'
INTO TABLE articles
CHARACTER SET utf8mb4
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n';

MySQLで日本語を扱う場合は utf8mb4 を使う。utf8 はMySQL独自の3バイトUTF-8(絵文字非対応)のため、utf8mb4 を使うのが現在の標準だ。

Windowsのツールに渡すCSVはShift_JISが求められることがある。その場合は CHARACTER SET sjis を指定する。

-- Shift_JIS(cp932)でエクスポート
SELECT * FROM products
INTO OUTFILE '/var/lib/mysql-files/products_sjis.csv'
CHARACTER SET sjis
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\r\n';  -- Windowsの改行コード

文字化けの確認と対処

# ファイルの文字コードを確認
file -i /var/lib/mysql-files/articles.csv
# application/csv; charset=utf-8

# nkfで変換(インストールが必要)
nkf -w --overwrite /var/lib/mysql-files/articles_sjis.csv  # UTF-8に変換

# iconvで変換
iconv -f SHIFT_JIS -t UTF-8 input.csv > output.csv

NULL値の扱い

MySQLはデフォルトでNULL値を \N として出力する。

-- NULLを空文字として出力したい場合
SELECT
  id,
  IFNULL(name, '') AS name,
  IFNULL(email, '') AS email
FROM users
INTO OUTFILE '/var/lib/mysql-files/users.csv'
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n';

インポート時に空文字をNULLとして扱いたい場合は SET 句で変換する。

LOAD DATA INFILE '/var/lib/mysql-files/users.csv'
INTO TABLE users
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n'
(@id, @name, @email)
SET
  id = @id,
  name = NULLIF(@name, ''),   -- 空文字をNULLに変換
  email = NULLIF(@email, '');

応用編: mysqldumpやmysqlimportとの使い分け

mysqldumpとの比較

観点 SELECT INTO OUTFILE mysqldump
出力形式 CSV/TSV(自由に設定可) SQL文(INSERT)
条件指定 WHERE句で柔軟に テーブル単位のみ
他DBへの移行 DB種別を問わず移行可 MySQL専用
日本語対応 CHARACTER SET指定必須 設定次第
ヘッダー行 UNION ALLで対応 なし
用途 データ加工・外部ツール連携 バックアップ・MySQL間移行

mysqlimportの使い方

mysqlimport はコマンドラインからLOAD DATA INFILEを実行するツールだ。ファイル名がテーブル名として扱われる。

# users.csv を users テーブルにインポート
mysqlimport \
  --user=your_user \
  --password \
  --local \
  --fields-terminated-by=',' \
  --fields-enclosed-by='"' \
  --lines-terminated-by='\n' \
  --ignore-lines=1 \
  database_name \
  /path/to/users.csv

--local オプションで LOAD DATA LOCAL INFILE 相当の動作になる(サーバ側のパスではなくクライアントのローカルパスを指定できる)。

シェルスクリプトで自動化

定期的なデータエクスポートをシェルスクリプトで自動化する例を示す。

#!/bin/bash
# export_daily.sh - 日次データエクスポート

DATE=$(date +%Y%m%d)
EXPORT_DIR="/var/lib/mysql-files"
DB_USER="export_user"
DB_NAME="production"
OUTPUT_FILE="${EXPORT_DIR}/orders_${DATE}.csv"

mysql -u "${DB_USER}" -p"${DB_PASS}" "${DB_NAME}" << EOF
SELECT
  order_id,
  customer_id,
  total_amount,
  created_at
FROM orders
WHERE DATE(created_at) = CURDATE() - INTERVAL 1 DAY
INTO OUTFILE '${OUTPUT_FILE}'
CHARACTER SET utf8mb4
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n';
EOF

echo "エクスポート完了: ${OUTPUT_FILE}"
echo "レコード数: $(wc -l < "${OUTPUT_FILE}")"

トラブルシュート

実際によく遭遇するエラーと解決策をまとめた。

エラー・症状 原因 解決策
ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option ファイルパスがsecure_file_privの設定外 SHOW VARIABLES LIKE 'secure_file_priv';で確認し、そのディレクトリにファイルを配置
ERROR 1045 (28000): Access denied for user FILE権限がない GRANT FILE ON *.* TO 'user'@'host'; で権限付与
ERROR 13 (HY000): Can't get stat of '/path/to/file' MySQLサーバがファイルを読めない ファイルのパーミッション確認(mysqlユーザが読み取れるか)
ERROR 1086 (HY000): File '/path/file.csv' already exists 出力先ファイルが既に存在する 既存ファイルを削除するかファイル名を変更する(MySQLは上書きしない)
CSVを開くと文字化けする 文字コードの不一致 CHARACTER SET utf8mb4(またはsjis)を明示する
インポートでWARNINGが大量に出る データ型の不一致・NULL扱い SHOW WARNINGS; で内容を確認しSET句で変換処理を追加
LOAD DATA LOCAL INFILE が動かない local_infileが無効 my.cnflocal_infile=ONに設定し再起動

まとめ

MySQLでのCSVファイル入出力には以下の2つのSQL文を使う。

  • エクスポート: SELECT ... INTO OUTFILE — WHERE句・JOIN・ORDER BYが使え、柔軟な条件でCSVに書き出せる
  • インポート: LOAD DATA INFILE — 大量データを高速にインポートでき、SET句でデータ変換も可能

MySQL 8.xではセキュリティ設定が強化されており、secure_file_priv の設定確認と FILE 権限の付与が必須だ。LOAD DATA LOCAL INFILE を使う場合はサーバとクライアントの両方で設定が必要になる。

日本語データを扱う場合は CHARACTER SET utf8mb4 を常に明示し、Windows向けCSVには \r\n の改行コードを指定することで文字化けや改行ズレを防げる。


関連記事

MySQL,SQLCSV,MySQL,SQL

Posted by GENDOSU