Bash
MySQL

MySQL へ大量のCSVファイルをbashでインポートする

大量のCSVファイルをインポートするためにシェル組んだので残しておく

MySQL8.0 で実行

$ mysql --version
mysql  Ver 8.0.11 for Linux on x86_64 (MySQL Community Server - GPL)

設定ファイルに以下を記述する必要があった
記述しないと 「 The used command is not allowed with this MySQL version 」って怒られる

/etc/my.cnf
[client]
loose-local-infile=1

もしかしたら以下も実行しとく必要があったかも

mysql> SET PERSIST local_infile= 1;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @@local_infile;
+----------------+
| @@local_infile |
+----------------+
|              1 |
+----------------+
1 row in set (0.00 sec)

mysql>

MySQL 8.0でLOAD DATA LOCAL INFILEが "ERROR 1148 (42000): The used command is not allowed with this MySQL version" で失敗する時
(参考にさせていただきました)


大量のCSVをインポートするシェル

実行ディレクトリにある ~.csv ファイルを全部読み込んでインポートします

#!/bin/bash

## INIT
PWD="PasSw0rd"
TOTAL=`ls *.csv | wc -l`

## テーブルクリア
echo 'TRUNCATE TABLE csv_table;' | MYSQL_PWD=$PWD mysql -u UserName DBName -s -N

## ディレクトリ内の CSV ファイルを対象にループ
ls `pwd`/*.csv | while read line
do
  ## CSV の行数取得
  L=`cat "$line" | wc -l`

  ## カウンタ
  cnt=`echo $(($cnt + 1))`

  ## 処理ファイル表示
  echo "$line" | awk -v"cnt=$cnt" -v"total=$TOTAL" -v"line=$L" '{ printf("%3d/%3d %\0477d %s\n", cnt, total, line, $0);}'

  ## インポート処理
  MYSQL_PWD=$PWD mysql -u UserName DBName << EOT
  LOAD DATA LOCAL INFILE '$line'
  INTO TABLE csv_table
  FIELDS
    TERMINATED BY ','
    ENCLOSED BY '"'
  LINES
    TERMINATED BY '\r\n'
  IGNORE 1 LINES;
EOT
done

## 結果行数表示
echo 'select count(*) from csv_table;' |
MYSQL_PWD=$PWD mysql -u UserName DBName -s -N |
awk '{printf("\nIMPORT %\047d  Record\n", $0);}'

CSVファイルの先頭1行を読み飛ばしてます( IGNORE 1 LINES )
CSVはカンマ区切り( TERMINATED BY ',' )
CSVはダブルクォーテーションで囲まれてる( ENCLOSED BY '"' )
CSVの行の終わりは改行 ( TERMINATED BY '\r\n' )
CSV ファイルはUTF8 で作ってありました

実行時には以下のような表示を行います

  1/128      56 /path/file1.csv
・・・・・
128/128   9,440 /path/file128.csv

IMPORT 201,289  Record

インポートしたデータのカラム毎のデータバリエーション数とレコード数確認

CSVにデータがあったのか、カラム毎にとりあえず確認してみる

#!/bin/bash

## INIT
PWD="PasSw0rd"

echo "desc csv_table;" |
MYSQL_PWD=$PWD mysql -u UserName DBName -s -N |
awk '{print $1;}' |
while read line
do

  col=$(echo $line | sed -e 's/\//_/g')
  echo "$line" | awk '{printf("SELECT `%s`, count(*) FROM csv_table GROUP BY `%s` ORDER BY 2 DESC, 1\n", $1, $1);}' |
  MYSQL_PWD=$PWD mysql -u UserName DBName -s -N >  "lst.$col"

  cnt=`cat "lst.$col" | wc -l`

  if [ $cnt -ne 1 ]
  then
    echo "$line" | awk -v"cnt=$cnt" '{printf("%\0477d %s\n", cnt, $0);}'
    if [ $cnt -lt 10 ]
    then
      cat "lst.$col" | awk '{printf("          : %s\n", $0);}'
    fi
  fi

done

カラム内のデータバリエーションがない場合は表示対象外にして( if [ \$cnt -ne 1 ] )
バリエーションが少ない場合はデータ内容毎のレコード数を表示してみる( if [ \$cnt -lt 10 ] )

実行結果はこんな感じ

109,790 xxキー
 40,927 xx番号
 46,699 xx名
 21,118 xxxx
     14 xxxxxx
    223 xxx
      2 xx種別
          :     201051
          : 001 238
  4,862 xxxxxxxxx
 ・・・・・

 xxキー は 109,790のバリエーションがあり
 xx種別 は 2 バリエーションあり、内容は 空白が 201,051レコード、種別 001 が 238レコード
のような表示になります