行数が30万件ほどあるCSVから、PHP経由でMysqlにデータを投入

このQ&Aのポイント
  • 行数が30万件ほどあるCSVから、PHP経由でMysqlにデータを投入しようとしています。
  • 2000件ごとにファイルを分割し、再度各ファイルを読み込みなおして、insertしようと考えました。
  • ファイル分割で2000件ごとに区切った場合、最後の2000件に満たない端数分をファイルに落とす方法がわかりません。
回答を見る
  • ベストアンサー

行数が30万件ほどあるCSVから、PHP経由でMysqlにデータを投入

行数が30万件ほどあるCSVから、PHP経由でMysqlにデータを投入しようとしています。 2000件ごとにファイルを分割し、再度各ファイルを読み込みなおして、insertしようと考えました。 ところが、ファイル分割で2000件ごとに区切った場合、最後の2000件に満たない端数分をファイルに落とす方法がわかりません。 どなたか教えていただけないでしょうか。 ※かなり冗長な書き方をしているかと思いますので、改善点等あればご指摘いただければ幸いです。 以下サンプルコード------------------------------------------------------ $count = "0"; $max = "2000"; //ファイルあたりの行数 $file_count = "0"; //ファイル名につける連番 $RF = fopen( "base_file.csv", "r") or die("ファイルが開けません"); while (($data = fgetcsv($RF)) !== false) { if($count < $max){ //順番を入れ替えたり、データをいじるかもしれないのでsprintf $lines.= sprintf("%s,%s,%s,%s,%s,%s,%s\n", $data[0], $data[1], $data[2], str_replace(" "," ",$data[3]), //全角スペースを半角に変換 $data[4], $data[5], $data[6] ); } if($count == $max){ //ファイル名生成 $fileno = zerofill($file_count); $filename = "./files/datafile_".$fileno.".dat"; $WF = fopen($filename, "w"); fputs($WF, $lines); fclose($WF); //書き込みデータを空にする $lines = ""; $file_count++; $count = "0"; } $count++; } fclose($RF); //ファイル名の連番を0で埋める function zerofill($val){ if($val < 100){ if($val < 10){ $ret = "0".$val; } $ret = "00".$val; } return $ret; }

  • PHP
  • 回答数5
  • ありがとう数12

質問者が選んだベストアンサー

  • ベストアンサー
  • duron
  • ベストアンサー率77% (73/94)
回答No.4

1で書いたようにwhileを抜けた後にcountをチェックしてください。 $countが0より大きいということはファイルに書き出されていないデータがあるということです。 ファイルポインタの位置を調べて~云々のことをすればループ内で出力できると思いますが まぁそこはあまり考えないようにしました。 以下修正(間違ってた箇所があったらゴメンネ)--------------------------------------- define("READ_FILE", "./csv/base_file.csv"); $i = "0"; //総行数カウンター $count = "0"; //処理件数カウンター $max = "50"; //ファイルあたりの行数 $file_count = "0"; //ファイル名につける連番 $line=""; //←コレを忘れないように $RF = fopen( READ_FILE, "r") or die("ファイルが開けません\n"); while (($data = fgetcsv($RF)) !== false) { $count++; //1行目はフィールド名だったのをすっかり忘れていたので追加 //2行目から処理 if($i > 0){ //フィールドのデータをいろいろいじるのでsprintf $lines.= sprintf("%s,%s\n", $data[0], $data[1] ); } if($count == $max){ //ファイル名生成 $fileno = sprintf("%03d",$file_count); $filename = "./files/datafile_".$fileno.".dat"; $WF = fopen($filename, "w"); fputs($WF, $lines); fclose($WF); //書き込みデータを空にする $lines = ""; $file_count++; $count = "0"; } $i++; } if($count == $max){ //←ココでチェックです //ファイル名生成 $fileno = sprintf("%03d",$file_count); $filename = "./files/datafile_".$fileno.".dat"; $WF = fopen($filename, "w"); fputs($WF, $lines); fclose($WF); } fclose($RF);

その他の回答 (4)

  • duron
  • ベストアンサー率77% (73/94)
回答No.5

おっと、下のwhile抜け後のcountチェックはif($count > 0){です、すいません。 忘れてましたが1つ目のファイルが1行少なくなるのを防ぐには if($i > 0){}の中にcount++;を移動してください。 これで「$lineに追加されている行数」という意味合いになります。

  • smileeeen
  • ベストアンサー率70% (21/30)
回答No.3

本題についてはNo.1、No.2さんが回答して下さっているので、 ちょっとしたアドバイスを。 zerofillという関数を自作されていますが、 通常の0フィルの処理であれば、PHPの組み込み関数で実現可能です。 $fileno = zerofill($file_count);  ↓ $fileno = sprintf("%04d", $file_count); これで4桁未満の値の場合は、上位の桁を0で埋めてくれます。 ご参考までに。

yasagure-kun
質問者

お礼

ありがとうございます。

  • duron
  • ベストアンサー率77% (73/94)
回答No.2

先ほど1で回答したものですが whileループ内で[$count == $max]となるのは一回前の処理が2000件目だったとき(ループの最後でインクリメントしているため)になります。 そのため2001件目のデータがファイルには保存されないことになってしまいます。 またループに入った際はちょうど2000件出力が行われた場合でもcountは必ず1以上になってしまいます。 そのためループ内の最初でcountをインクリメントする(今処理しているデータはファイルに書き込むn件目という意味合いにする)といいと思います。 また、最初のif($count < $max)は不要($maxに到達した時点で0クリアされるのだから条件は必ず満たす)です。

yasagure-kun
質問者

お礼

ありがとうございます。いただいたアドバイスを元に書き直しました。 サンプルデータの数を減らしてテストをしてみました。 base_file.csv:153行分のテキストファイル また、すっかり忘れていましたが、1行目はフィールド名なので、必要データは2行目からと なります。 $count加算の直後に、2行目以上の判定を追加しています。 今回の趣旨とは異なりますが、この場合1つ目のファイルだけは49行なんですよね。 どうしたらいいかわかりませんでしたが、とりあえずデータに抜けはなかったのでよしとします。 ただ、[$count == $max]では、1行目~100行目までしかファイル書き出しができず、 残りの53行分を判定処理する方法がわかりません。 $countが$maxに達したかの判定の次に、残りの分をどうにかする処理を入れるのかと思いますが、 どうしたらいいのでしょうか。 以下サンプルコード--------------------------------------- define("READ_FILE", "./csv/base_file.csv"); $i = "0"; //総行数カウンター $count = "0"; //処理件数カウンター $max = "50"; //ファイルあたりの行数 $file_count = "0"; //ファイル名につける連番 $RF = fopen( READ_FILE, "r") or die("ファイルが開けません\n"); while (($data = fgetcsv($RF)) !== false) { $count++; //1行目はフィールド名だったのをすっかり忘れていたので追加 //2行目から処理 if($i > 0){ //フィールドのデータをいろいろいじるのでsprintf $lines.= sprintf("%s,%s\n", $data[0], $data[1] ); } if($count == $max){ //ファイル名生成 $fileno = sprintf("%03d",$file_count); $filename = "./files/datafile_".$fileno.".dat"; $WF = fopen($filename, "w"); fputs($WF, $lines); fclose($WF); //書き込みデータを空にする $lines = ""; $file_count++; $count = "0"; } //ここでもう一度判定? $i++; } fclose($RF);

  • duron
  • ベストアンサー率77% (73/94)
回答No.1

whileループ内でcountがmaxに到達しないうちにループを抜ける条件($dataが取得できない)になってしまうための問題です。 whileループを抜けた後にcountが0より大きかったらループ内のif($count == $max)でやっている処理を行うようにしたらどうでしょうか?

関連するQ&A

  • phpでcsvデータをランダム表示について

    下記はcsvデータをランダムに1つだけ抽出するphpのプログラムですが、1度表示したデータに色をつけるとか、表示させない方法はありますか? <!doctype html> <html> <head> <meta charset="UTF-8"> <title>無題ドキュメント</title> </head> <body> <?php // 抽出数 $count = 1; $list = array(); // 読み込みCSVファイル $lines = file('test.csv'); shuffle($lines); foreach ($lines as $line) { if ( ! in_array($line, $list)) { array_push($list, $line); if (count($list) >= $count) { break; } } } // CSVファイルに出力 $file = fopen('test2.csv', 'w'); foreach ($list as $l) { echo $l.'<br>'; if ($file) { fputs($file, $l); } } fclose($file); ?> </body> </html>

    • ベストアンサー
    • PHP
  • PHPとMySQLで100万件のデータを登録

    PHPとMySQLで100万件のデータを登録する方法に困っております。 1.PHPで100万回ループして数字をランダムで取得し一意の数字を配列に格納。 ⇒データ量が多すぎてタイムアウトしてしまいます。いい方法はありますか? while($i<1000000){ $h = sprintf("%07d",mt_rand(1,1000000)); if(!array_search($h,$val)){      array_push($val,$h);      $i++; } } 2.またデータベースはMySQLを使用しようかと考えていますが、一気に登録する方法など効率のいいやり方はありますでしょうか?

    • ベストアンサー
    • PHP
  • PHPによる行データのアップダウン

    PHPによる行データのアップダウン フォームからデータ行をGETした内容をアップダウンさせたいのですが、どうにも入れ替えができません。 プログラムコードのアドバイスをください。 *ログファイルの中(data.dat) ゴリラ チューリップ ばなな ストロベリー ペンギン <?php $data = "data.dat"; function row_up() { $lines = @file($data); $lines[$ont] = $lines[$_GET[row]]; //自分 $lines[$_GET[row]] = $lines[$_GET[row]-1]; //前の人 $fp = fopen($data,"w"); rewind($fp); for($i=0;$i<count($lines);$i++) { if($lines[$i] == $lines[$_GET[row]]){ fputs($fp,$lines[$_GET[row]]); } elseif($lines[$i] == $lines[$ont]){ fputs($fp,$lines[$ont]); } fputs($fp,$lines[$i]); } fclose($fp); } function row_down() { $lines = @file($data); $lines[$ont] = $lines[$_GET[row]]; //自分 $lines[$_GET[row]] = $lines[$_GET[row]+1]; //次の人 $fp = fopen($data,"w"); rewind($fp); for($i=0;$i<count($lines);$i++) { if($lines[$i] == $lines[$ont]){ fputs($fp,$lines[$ont]); } elseif($lines[$i] == $lines[$_GET[row]]){ fputs($fp,$lines[$_GET[row]]); } fputs($fp,$lines[$i]); } fclose($fp); } switch($_GET[mode]) { case up: row_up(); break; case down: row_down(); break; default: break; } ?>

    • ベストアンサー
    • PHP
  • ファイルの入出力で困っています(C言語)

    はじめまして、nathan3と申します。 昔、さらっとC言語を学んでいたので、仕事場でも活用できればと思い、勉強しなおしています。 以下のプログラムですが、コンパイルはするものの、実行がなされません。 sprintfをつかってファイルを作り、fprintfで読み込み、countで繰り返し別名のファイルを読み込み・作成し…といったプログラムを書いているつもりです。 調べながら書いた稚拙なプログラムですが、ここがちがう!というところをお教えいただけると大変助かります。 #include <stdio.h> int main(void){ FILE *fp,*fo; char *fname1; char *fname2; char s[100],t[100]; int ret,count; for(count = 0 ; count < 3 ; count++) { sprintf(fname1, "sankasha%d.txt", count); fp = fopen(fname1, "r"); if (fp == NULL){ printf("%s can't open a file\n", fname1); return -1; } sprintf(fname2, "matome%d.txt", count); fo = fopen(fname2,"w"); if (fo == NULL){ printf("%s can't open a file\n", fname2); return -1; } printf("--fscanf---"); while( (ret = fscanf(fp,"%[^,],%s", s, t)) != EOF ){ fprintf(fo,"%s ", t); } } fclose(fo); fclose(fp); return 0; } 何度見直しても間違いが見つからず困窮しております。 どうぞ、みなさまのお力をお貸しください! よろしくお願いいたします。

  • PHPからMySQL CSVファイルのアップロード

    こんにちわ。 現在、XAMPP(1.6)にて開発をおこなっています。 MySQL Ver 5.0.51 PHP 5.2.5 Web上からcsvファイルを参照して、作成してあるDB(フィールド数32)へアップロードを おこないたいのですが、タイムアウトエラーになってしまいます。 アップするCSVファイルが54M(8万行)ほどあるため、php.ini「uplaod_max_file_size」 増やしてみたり、タイムアウト時間を長くしてみても、INSERTではタイムアウトになって しまいます。 そのため、LOADで読込もうとしたのですが以下のようなエラーになってしまいます。 PHP初めてで'や"の使い方が間違っているのかも知れませんが、どこが間違っているのか 教えていただけますでしょうか? ↓エラー文 Parse error: syntax error, unexpected T_VARIABLE in C:\xampp\htdocs\dmti.php on line 24 ↓実行文(24行目前後)$polはテーブル名 mysql_query("set names sjis"); $sql ="load data infile '$fl' into tabale $pol fields terminated by ',' enclosed by '"' lines terminated by '\r\n';" $ret = mysql_query($sql, $db); if($ret){ print mysql_num_rows($db)."件の登録を完了しました。.<br><br>"; $body = "<br><br> <input type='button' value='次へ' onclick='window.location=\"news.php\"'> <input type='button' value='ホームへ戻る' onclick='window.location=\"index.htm\"'>"; }else{ print "登録に失敗しました。"; $body .= "<br><br> <input type='button' value='戻る' onclick='history.back()'> <input type='button' value='ホームへ戻る' onclick='window.location=\"index.htm\"'>"; } 以上、宜しくお願い致します。

    • ベストアンサー
    • PHP
  • PHPでCSVをへんしゅうするには?

    CSVで編集をする際の動作にどうしても詰まってしまい困っています。 いろんなことを試したのですがうまくいきません。 追加と削除ができなので、それを合体すれば出来るのかなって思ったのですが、 ファイルの中身が消えてしまって、先に進まない状態なのです。 // 変更 if(isset($_POST["submit_upd"])){ // 押下したボタン番号を取得 $upd = isset($_POST["sbumit_upd_del"])? $_POST["sbumit_upd_del"]: ""; // POSTされたデータを取得 $number = $_POST["number"]; $category = htmlspecialchars($_POST["category"], ENT_QUOTES); $dating = htmlspecialchars($_POST["dating"], ENT_QUOTES); //csvファイルに書き込み $data_all = @file($filecsv); $file_csv = @fopen($filecsv, "w"); flock($file_csv,2); for($i=0; $i<count($data_all);$i++){ $data_array = explode(",", $data_all[$i]); if($data_array[0] != $upd){ //csvファイルに書き込み $current = @file_get_contents($filecsv); $current .= "$number,$category,$dating\n"; file_put_contents($filecsv, $current); } } flock($file_csv,3); fclose($file_csv); unset ($data_all); header("Location: index.php"); exit; }

    • ベストアンサー
    • PHP
  • MYSQLからCSVへの書き込み方法

    MYSQLからCSVへの書き込み方法で質問があります。 下記のコードを記載しましたが DBのデータ件数分の改行しか出力されませんでした。 ブラウザ上でecho動作させた際には、 echo "<p>CSVファイルにセットされるデータ:".$string; でしっかりデータが設定されていました 何が悪いですか? ////////////////////////////////////////////////////////////////// // DBからCSVファイル書き込み(全件新規) // $file_name:csvファイル名 ////////////////////////////////////////////////////////////////// function DB_csvSet($file_name, $dbhost=DBHOST, $dbuser=DBUSER, $dbpass=DBPASS, $dbname=DBNAME) { print "DB_csvSet start"; // MySQL 接続 if (!($cn = mysql_connect($dbhost, $dbuser, $dbpass))) { die("DB_csvSet Error: mysql_connect"); } //MySQLのクライアントの文字コードをsjisに設定 mysql_query("SET NAMES sjis") or die("can not SET NAMES sjis"); // MySQL DB 選択 if (!(mysql_select_db($dbname))) { die("DB_csvSet Error: mysql_select_db"); } // MySQL 問い合わせ $sql = sprintf("select * from %s", TABLE_NAME); if (!($rs = mysql_query($sql))) { die("DB_csvSet Error: mysql_query"); } // ファイルを新規モードで開く $file = fopen($file_name, "w") or die("OPENエラー $file_name"); // ファイルをロックする flock($file, LOCK_EX); // MySQL レコード参照 while ($item = mysql_fetch_array($rs)) { printf("id=%s ", $item[ITEM_ID]); printf("name=%s ", $item[ITEM_NAME]); printf("point=%s<BR> ", $item[ITEM_POINT]); // 3つの変数から配列を作成する $array = compact($item[ITEM_ID], $item[ITEM_NAME], $item[ITEM_POINT]); // $array = compact("id","name","point"); // 配列のデータをカンマで区切って結合する $string = implode(",", $array); // 結合された文字列を表示する echo "<p>CSVファイルにセットされるデータ:".$string; // データを書き込む fputs($file, $string."\n"); } // ロックを解除する flock($file, LOCK_UN); // ファイルを閉じる fclose($file); // MySQL 切断 mysql_close($cn); // 正常終了 print "正常終了"; }

    • 締切済み
    • PHP
  • 構造体のファイル書き込み&読み出しに関して2

    C言語を勉強しているものです。指定した番号に構造体を書き込み、指定した番号をの構造体を出力するプログラムを作成したいのですが、表示結果画像のようになってしまいます。 デバックしても、どこが違うのかがわかりません。説明不足かとは思いますがご教授お願いします。 ↓↓ソースコード↓↓ #include<stdio.h> #include<stdlib.h> struct S_data{ char Name[10+1];/*名前*/ int Sex;/*性別*/ int Height;/*身長*/ float Weight;/*体重*/ }; void FR_data(FILE *Fp,int pos); void FW_data(FILE *Fp,int pos); void OUP_data(struct S_data tag); void INP_data(struct S_data *tag); int RF_data(FILE *Fp,struct S_data *tag,int pos); int WF_data(FILE *Fp,struct S_data *tag,int pos); void main(){ FILE *Fp; int pos=0; int Ret; Fp=fopen("aaa.dat","r+b"); if(Fp==NULL){ Fp=fopen("aaa.dat","w+b"); if(Fp==NULL){ printf("File not open\n"); exit(2); } } while(1){ printf("入力の番号[0:終了]->"); scanf("%d",&pos); if(pos==0) break; FW_data(Fp,pos); } while(1){ printf("出力の番号[0:終了]->"); scanf("%d",&pos); if(pos==0) break; FR_data(Fp,pos); } Ret=fclose(Fp); } void FR_data(FILE *Fp,int pos){ struct S_data Temp; /*出力情報*/ int Ret; /*返却値*/ memset(&Temp,'\0',sizeof(Temp)); Ret=RF_data(Fp,&Temp,pos); /*情報の読み込み*/ if (Ret!=1){ printf("File not read\n"); }else{ OUP_data(Temp); /*情報の表示*/ } } void FW_data(FILE *Fp,int pos){ struct S_data wk; /*入力情報*/ int Ret; /*返却値*/ memset(&wk,'\0',sizeof(wk)); INP_data(&wk); /*情報の入力*/ Ret=WF_data(Fp,&wk,pos); /*情報の書き込み*/ if (Ret!=1){ printf("File not write\n"); } } void OUP_data(struct S_data tag){ printf("Name:%s\n",tag.Name); if (tag.Sex==0){ printf("Sex:M\n"); }else{ printf("Sex:F\n"); } printf("Height:%d\n",tag.Height); printf("Weight:%.2f\n",tag.Weight); } void INP_data(struct S_data *tag){ memset(tag,'\0',sizeof(tag)); printf("Name-->"); scanf("%s",&tag->Name); printf("Sex[0:M1:F]-->"); scanf("%d",&tag->Sex); printf("Height-->"); scanf("%d",&tag->Height); printf("Weight-->"); scanf("%f",&tag->Weight); } int RF_data(FILE *Fp,struct S_data *tag,int pos){ int Ret_I; /*fseek返却値*/ size_t Ret_S; /*fread返却値*/ Ret_I=fseek(Fp,sizeof(tag)*(pos),SEEK_SET); Ret_S=fread(tag,sizeof(tag),1,Fp); return Ret_S; } int WF_data(FILE *Fp,struct S_data *tag,int pos){ int Ret_I; /*fseek返却値*/ size_t Ret_S; /*fwrite返却値*/ Ret_I=fseek(Fp,sizeof(tag)*(pos),SEEK_SET); Ret_S=fwrite(tag,sizeof(tag),1,Fp); return Ret_S; }

  • PHPとCSVの表示 もっとスマートにしたい

    現在PHPに手を出し始めた初心者です まだ分からない事だらけですが、できれば教えてください CSVファイルからデータを読み込んで表示するということを 苦戦しながらやっております ただ、1つのPHPファイルだけでの表示方法がわからず、今は3つのPHPファイルにしています 目的は大区分を選択後、小区分の一覧が表示され、小区分の1つを選択すると詳細が表示される という感じのものです まずCSVファイル 大区分,小区分,名称,詳細 1,101,hoge1,詳細1 1,102,hoge2,詳細2 2,201,hogex1,詳細x1 2,202,hogex2,詳細x2 3,301,hogexx1,詳細xx1 という感じに分けています で1つ目のhoge0.phpで <a href="hoge1.php?code=1">大区分(1)</a> ・・・ code が 1 なら hoge1.php で 小区分 1 の一覧を表示します 次に2つ目のhoge1.phpで $code = $_GET['code']; $lines = file('hoge.csv'); foreach($lines as $line) { $line = rtrim($line); list($bno,$sno,$name,$detail) = explode(",", $line); if ($bno==$code) { echo "<a href=hoge2.php?type=$sno>詳細</a>".$name."<br>"; } } type が 101 なら hoge2.php で 小区分 101 にある hoge1 項目の詳細を表示します 最後3つ目のhoge2.phpで $type = $_GET['type']; $lines = file('hoge.csv'); foreach($lines as $line) { $line = rtrim($line); list($bno,$sno,$name,$detail) = explode(",", $line);{ if ($no==$type) { echo $name.$detail; } } } 一応これでやりたい事は実現できたのですが、あまりにも不恰好で・・・ これを1つのPHP内で処理できるようにするにはどうしたらいいでしょうか? スマートになるやり方があれば教えてください

    • ベストアンサー
    • PHP
  • php mysql INSERT

    mysqlのデータ登録の際に値が空だとエラー文が表示されてしまいます。 エラー文は You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1 にでます。 なお、mysqlのバージョンは5.1です。 試行錯誤で試したのですが、なかなかできないためわかる方がいましたら宜しくお願いします。 ソースは foreach((array) $test1 as $key => $val){ if($val !== "" && $test2[$key] !== "") { $vals .= ","; $vals .= "('$val','$test2[$key]')"; } } if($vals !== "") { $vals = trim($vals,','); $testQuery = sprintf("INSERT INTO `home_scorer` (`test1`,`test2`) VALUES" .$vals); $testResult = mysql_query($testQuery, $link) or die(mysql_error()); } test1 と test2に値が入っていれば登録はさせるのですが、値が入っていないときはINSERTしない状態にできればと思います。 宜しくお願いします。

    • 締切済み
    • PHP

専門家に質問してみよう