※この記事にはアフィリエイト広告(プロモーション)が含まれます。
MySQL で CSV を表に取り込むには LOAD DATA を使います。ファイルをサーバーが読む LOAD DATA INFILE と、クライアントが読んで送る LOAD DATA LOCAL INFILE の2つがあり、必要な設定もエラーの扱いも違います。この記事の出力は MySQL 8.4.11(Ubuntu 26.04 の標準パッケージ)で採りました。
まず見る(結論)
LOAD DATA INFILE '/var/lib/mysql-files/item.csv'
INTO TABLE item
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 LINES;
LOAD DATA INFILEのファイルはsecure_file_privのディレクトリ(Ubuntu では/var/lib/mysql-files/)に置くLOCALはサーバーのlocal_infile=ONとクライアントの--local-infile=1の両方が要るLOCALは不正な行があっても止まらず、警告を出して値を変えて入れる。取り込んだらSHOW WARNINGSを見る- Windows で作った CSV は
LINES TERMINATED BY '\r\n'
エラー別の原因
| エラー | 原因 | 対処 |
|---|---|---|
ERROR 1290 … --secure-file-priv option |
ファイルが secure_file_priv の外にある |
/var/lib/mysql-files/ に置く/LOCAL を使う |
ERROR 1045 … Access denied for user 'app1'@'localhost' |
FILE 権限が無い(ログインはできている) |
GRANT FILE ON *.* TO .../LOCAL を使う |
ERROR 3948 … Loading local data is disabled |
サーバーの local_infile が OFF |
SET GLOBAL local_infile = ON |
ERROR 2068 … file request rejected due to restrictions on access |
クライアントで --local-infile を付けていない |
mysql --local-infile=1 |
ERROR 1366 … Incorrect integer value: 'id' |
見出し行を値として読んだ | IGNORE 1 LINES |
ERROR 1366 … '21' |
先頭に BOM がある | BOM を外す/見出し行ごと IGNORE 1 LINES で飛ばす |
ERROR 1300 … Invalid utf8mb4 character string |
Shift_JIS の CSV | CHARACTER SET cp932 |
ERROR 1062 … Duplicate entry '1' |
主キーの重複 | IGNORE か REPLACE |
ERROR 1045 はパスワード違いと同じメッセージですが、-e の中身を実行する段階で出ています。
LOAD DATA INFILE(サーバーが読む)
SHOW VARIABLES LIKE 'secure_file_priv';
+------------------+-----------------------+
| Variable_name | Value |
+------------------+-----------------------+
| secure_file_priv | /var/lib/mysql-files/ |
+------------------+-----------------------+
このディレクトリは mysql ユーザーしか入れない(drwx------ mysql mysql)ので、root でコピーして所有者を mysql にしました。/tmp などに置いたまま実行すると、次のエラーになります。
ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement
取り込んだ CSV と結果は次のとおりです。
id,name,price,created
1,りんご,120,2026-10-01
2,"みかん, 箱",980.5,\N
3,,0,2026-10-03
+----+----------------+--------+------------+
| id | name | price | created |
+----+----------------+--------+------------+
| 1 | りんご | 120.00 | 2026-10-01 |
| 2 | みかん, 箱 | 980.50 | NULL |
| 3 | | 0.00 | 2026-10-03 |
+----+----------------+--------+------------+
NULL にしたいフィールドは \N と書きます。空のフィールド(3行目の name)は NULL ではなく空文字になりました。
LOAD DATA LOCAL INFILE(クライアントが読む)
MySQL 8.4 の既定では local_infile が OFF です。サーバーとクライアントの組み合わせで結果が変わりました。
サーバー local_infile |
クライアント --local-infile=1 |
結果 |
|---|---|---|
| OFF | なし | ERROR 3948 |
| OFF | あり | ERROR 3948 |
| ON | なし | ERROR 2068 |
| ON | あり | 取り込める |
SET GLOBAL local_infile = ON;
$ mysql -u app1 -p --local-infile=1 -e "LOAD DATA LOCAL INFILE '/tmp/fsvmy/item.csv' INTO TABLE fsv.item ..."
SET GLOBAL は再起動で OFF に戻ります。どの PC からでも任意のファイルを送れる設定なので、作業が終わったら OFF に戻しました。
LOCAL では不正な行が警告だけで入る
型の違う値・列の過不足・長すぎる値を混ぜた CSV で比べました。
id,name,price,created
10,a,1,2026-10-01
11,b,x,2026-10-01
12,c,3
13,d,4,2026-10-01,extra
14,e,,2026-10-01
15,ffffffffffffffffffffffffff,5,2026/10/01
LOAD DATA INFILE は2行目で止まり、1行も入りませんでした。
ERROR 1366 (HY000): Incorrect decimal value: 'x' for column 'price' at row 2
LOAD DATA LOCAL INFILE はエラーにならず、6行とも入りました。
SHOW WARNINGS;
| Warning | 1366 | Incorrect decimal value: 'x' for column 'price' at row 2 |
| Warning | 1261 | Row 3 doesn't contain data for all columns |
| Warning | 1262 | Row 4 was truncated; it contained more data than there were input columns |
| Warning | 1366 | Incorrect decimal value: '' for column 'price' at row 5 |
| Warning | 1265 | Data truncated for column 'name' at row 6 |
+----+----------------------+-------+------------+
| id | name | price | created |
+----+----------------------+-------+------------+
| 10 | a | 1.00 | 2026-10-01 |
| 11 | b | 0.00 | 2026-10-01 |
| 12 | c | 3.00 | NULL |
| 13 | d | 4.00 | 2026-10-01 |
| 14 | e | 0.00 | 2026-10-01 |
| 15 | ffffffffffffffffffff | 5.00 | 2026-10-01 |
+----+----------------------+-------+------------+
LOCAL は IGNORE を付けたのと同じ扱いになり、LOAD DATA INFILE ... IGNORE INTO TABLE でも同じ警告の一覧になりました。mysql コマンドの -e で実行したときは件数も警告の有無も表示されなかったので、同じ接続の中で SHOW WARNINGS を続けて打ちます。
改行コード・BOM・文字コード
CRLF の CSV を LINES TERMINATED BY '\n' のまま入れると、最後の列の末尾に \r(0D)が残りました。
SELECT id, created, HEX(created), LENGTH(created) FROM item_s;
| 1 | 2026-10-01 | 323032362D31302D30310D | 11 |
文字列の列なので見た目では分からず、WHERE created = '2026-10-01' に当たらなくなります。LINES TERMINATED BY '\r\n' にすると 10 バイトになりました。最後の列が DATE 型の表では、\r があってもエラーにならず、日付として入りました。
Shift_JIS の CSV に対して CHARACTER SET cp932 を付けると、utf8mb4 の列に「あい」として入りました。
LOAD DATA INFILE '/var/lib/mysql-files/sjis.csv' INTO TABLE item CHARACTER SET cp932 FIELDS TERMINATED BY ',';
重複・列の並び替え・変換
LOAD DATA INFILE '...' IGNORE INTO TABLE item ...; -- 重複した行は飛ばす
LOAD DATA INFILE '...' REPLACE INTO TABLE item ...; -- 重複した行は CSV の値で置き換える
LOAD DATA INFILE '/var/lib/mysql-files/item.csv' INTO TABLE item
FIELDS TERMINATED BY ',' ENCLOSED BY '"' IGNORE 1 LINES
(id, name, @price, @created)
SET price = NULLIF(@price, ''), created = STR_TO_DATE(@created, '%Y-%m-%d');
CSV の列を @変数 で受けて SET で変換すると、空欄を NULL にしたり、日付の形式を変えたりできます。列の並びが表と違うときも、ここに CSV の順で列名を書きます。
CSV に出力する
SELECT * FROM item INTO OUTFILE '/var/lib/mysql-files/out.csv'
FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n';
出力先も secure_file_priv の中に限られます(外は ERROR 1290)。同じ名前のファイルがあると ERROR 1086 (HY000): File '/var/lib/mysql-files/out.csv' already exists になり、上書きはされません。NULL は \N で出ました。
関連: MySQLのERROR 1062「Duplicate entry」 / MySQLのERROR 1366「Incorrect string value」 / PostgreSQLでCSVを入出力する(COPY)