2013年7月13日土曜日

CSVをPHPでMysqlにLocalからImportする方法

ローカルのCSVをMysqlへアップロードする

それは簡単にみえるMysqlの処理が用意されている「 LOAD DATA LOCAL INFILE $file INTO TABLE tb1 FIELDS TERMINATED BY ','
などとPHPを使うと、エンコードの問題や、サーバの.iniの問題などでなかなかシンプルにいかず、何より原因の解明が非常にややこしい。

問題は、文字化け一つとってもPHPなのかMysqlなのか、はたまたCSVなのかわかりにい。

テキストで同じものを用意したりしてかなり手こずった。
あとは解決して納品したとして、クライアントが使うCSVの形式を指定していたのでは担当者が変わったり、6ヶ月ごとの商品の入れ替えの度に問い合わしの電話が鳴り、その度に仕様書を見るのはこちらなので、不安要素は最大限取り除く目的がある。

設計

●ローカルからCSVデータをPHPを使って引き込む


 //フォームから送られたデータは、$_FILES["csvfile"]["tmp_name"]で受け取ることができる。
$tmp = fopen($_FILES['csvfile']['tmp_name'], "r");

fopen()関数で、ファイルを開く。"r"というのは「読み込み専用」という指示です。
//ここで2次元配列にする。fgetcsv()関数により、csvデータを読み込む。

mb_convert_variables("UTF-8", "SJIS-win", $csv);
//文字化け防止のため、UTF-8に変換する// 配列 $csv の文字コードをSJIS-winからUTF-8に変換

$lim = count($csv);
//for文で読み込むために、配列の最大行数を出す

ここまででローカルから対象のCSVを引き込み、ファイルを開き文字化け防止のエンコード処理を施し、for,foreach,whileなどで回すための最大値を計算しています。


●今回はforでCSVから取り出した連想配列をとりだす。ポイントはimplodeでMysqlに挿入すする形式に変換している


//iに0を代入、$limに入れてある行数の最大数までループさせている、i++で1回のループごとにiの値が増えていく。
for($i=0; $i<=$lim; $i++){


//最後のループは多いので減らす
if($lim > ($i+1)){
$ar = $csv[$i];
$aaa = implode("\",\"", $ar);
//echo $aaa,"\n";//確認用書き出し

}


これでCSVの各行がカンマで区切られ、Mysql文に代入する用意が完了。

●Mysqlを接続し、単純にSQLをmysql_queryでDBへとインポートさせる。


//必要Mysql情報
$host = "localhost";
$mysql_user = "user";
$mysql_password = "pass";
$db = "db";
$tb = "tb1";
 

//接続
$cn = mysql_connect($host,$mysql_user,$mysql_password);
if(!$cn){
    die("db connect Error");
}
if(!(mysql_select_db("$db"))){
    die("db select error");
}



//今回はとにかくシンプルに設定を少なくするため、INSERTで挿入することにします。
$sql = "INSERT INTO $tb VALUES(\"$aaa\");";



//echo $sql;//確認用書き出し
$query = mysql_query($sql);
mysql_close($cn);//Mysqlの接続を閉じる
}


}//ifを閉じる



全文はこちら

データ選択用のhtmlフォーム
<form method="post" action="ファイル名.php" enctype="multipart/form-data">
<input type="file" name="csvfile" /><br />
<input type="submit" name="csvsub" value="csv呼出" /><br />
</form>

<?php
//フォームから送られたデータは、$_FILES["csvfile"]["tmp_name"]で受け取ることができる。
$tmp = fopen($_FILES['csvfile']['tmp_name'], "r");

while ($csv[] = fgetcsv($tmp, "1024")) {}
//fopen()関数で、ファイルを開く。"r"というのは「読み込み専用」という指示です。
//ここで2次元配列にする。fgetcsv()関数により、csvデータを読み込む。

mb_convert_variables("UTF-8", "SJIS-win", $csv);
//文字化け防止のため、UTF-8に変換する// 配列 $csv の文字コードをSJIS-winからUTF-8に変換


$lim = count($csv);//for文で読み込むために、配列の最大行数を出す

for($i=0; $i<=$lim; $i++){
if($i < ($lim-1)){
$ar = $csv[$i];
$aaa = implode("\",\"", $ar);
//echo $aaa,"\n";

$host = "サーバホスト";
$mysql_user = "ユーザ名";
$mysql_password = "パスワード";
$db = "データベース名";
$tb = "テーブル名";

$cn = mysql_connect($host,$mysql_user,$mysql_password);
if(!$cn){
    die("db connect Error");
}
if(!(mysql_select_db("$db"))){
    die("db select error");
}

$sql = "INSERT INTO tb1 VALUES(\"$aaa\");";

echo $sql;
$query = mysql_query($sql);
mysql_close($cn);
}

}
?>

追記(201409)
mysql関数が非推奨になったため、MySQLにはPDOなどで接続を強くお勧めします。
(ほとんどのレンタルサーバではPHP5.3以上となっている為、接続エラーが返されます)

【修正方法】 
全体の流れはほとんど一緒、接続方法を変更するだけでも利用できます。

 (修正前)



(修正後)

query("SELECT 'Hello, World.' AS msg FROM DUAL"); $row = $rec->fetch_array(MYSQLI_ASSOC); echo htmlentities($row["msg"]); $rec->free(); $con->close(); ?>