2010-08-04 7 views
6

Ich habe eine CSV-Datei, die 3,5 Millionen Codes enthält.
Ich sollte darauf hinweisen, dass dies nur einmal dieses einmal sein wird.Dieser Code muss über 3,5 Millionen Zeilen durchlaufen, wie kann ich ihn effizienter machen?

Die csv sieht aus wie

age9tlg, 
rigfh34, 
... 

Hier ist mein Code:

ini_set('max_execution_time', 600); 
ini_set("memory_limit", "512M"); 
$file_handle = fopen("Weekly.csv", "r"); 
while (!feof($file_handle)) { 
    $line_of_text = fgetcsv($file_handle); 

    if (is_array($line_of_text)) 
     foreach ($line_of_text as $col) { 
      if (!empty($col)) { 
       mysql_query("insert into `action_6_weekly` Values('$col', '')") or die(mysql_error()); 
      } 
    } else { 
     if (!empty($line_of_text)) { 
      mysql_query("insert into `action_6_weekly` Values('$line_of_text', '')") or die(mysql_error()); 
     } 
    } 
} 
fclose($file_handle); 

Ist dieser Code geht auf einen Teil des Weges durch mich sterben? Werden mein Speicher und die maximale Ausführungszeit hoch genug sein?

Hinweis: Dieser Code wird auf meinem lokalen Host ausgeführt, und die Datenbank befindet sich auf demselben PC, so dass die Latenz kein Problem ist.


Update:
hier ist eine weitere mögliche Implementierung. Dies tut man es in loser Schüttung Einsätze von 2000 Aufzeichnungen

$file_handle = fopen("Weekly.csv", "r"); 
$i = 0; 
$vals = array(); 
while (!feof($file_handle)) { 
    $line_of_text = fgetcsv($file_handle); 

    if (is_array($line_of_text)) 
     foreach ($line_of_text as $col) { 
      if (!empty($col)) { 
       if ($i < 2000) { 
        $vals[] = "('$col', '')"; 
        $i++; 
       } else { 
        $vals = implode(', ', $vals); 
        mysql_query("insert into `action_6_weekly` Values $vals") or die(mysql_error()); 
        $vals = array(); 
        $i = 0; 
       } 
      } 
     } else { 
     if (!empty($line_of_text)) { 
      if ($i < 2000) { 
       $vals[] = "('$line_of_text', '')"; 
       $i++; 
      } else { 
       $vals = implode(', ', $vals); 
       mysql_query("insert into `action_6_weekly` Values $vals") or die(mysql_error()); 
       $vals = array(); 
       $i = 0; 
      } 
     } 
    } 
} 
fclose($file_handle); 

wenn ich diese Methode zu verwenden, was der höchste Wert ist ich es auf einmal einzufügen setzen könnte?


Update 2
so fand ive i

LOAD DATA LOCAL INFILE 'C:\\xampp\\htdocs\\weekly.csv' INTO TABLE `action_6_weekly` FIELDS TERMINATED BY ';' ENCLOSED BY '"' ESCAPED BY '\\' LINES TERMINATED BY ','(`code`) 

verwenden können, aber das Problem ist jetzt, dass ich über das CSV-Format falsch war, es ist tatsächlich 4-Codes und dann ein Zeilenumbruch, so fhroflg, qporlfg, vcalpfx, rplfigc,
vapworf, flofigx, apqoeei, clxosrc,
...

also muss ich in der Lage sein, zwei LINES TERMINATED BY
diese Frage wurde verzweigt zu Here.


Update 3
einstellen es bulk Einsätze von 20k Reihen zu tun,

while (!feof($file_handle)) { 
    $val[] = fgetcsv($file_handle); 
    $i++; 
    if($i == 20000) { 
     //do insert 
     //set $i = 0; 
     //$val = array(); 
    } 
} 

//do insert(for last few rows that dont reach 20k 

verwenden, aber es stirbt an dieser Stelle, weil aus irgendeinem Grund $ val enthält 75k Zeilen und Idee, warum?
Beachten Sie, dass der obige Code vereinfacht ist.

+1

Es sollte offensichtlich sein, dass es extrem ineffizient ist, 3,5 Millionen Datensätze nacheinander einzufügen. SQL Server verfügt über eine spezielle Bulk-Copy-Semantik für große Masseneinfügungen. Sie sollten nach etwas Ähnlichem in MySQL suchen. – mquander

+0

Sie könnten es durch Teile wie X Anzahl der Einträge alle X Minuten ausführen, wenn Sie nicht alle auf einmal ausführen müssen. http://dev.mysql.com/doc/refman/5.0/en/mysqlimport.html – Prix

+0

Also, ich habe versucht, eine Masseneinfügung auf 200k Reihen, yeah interessanter Fehler; MySQL-Server ist weg lol – Hailwood

Antwort

21

Ich bezweifle, dass dies die populäre Antwort sein wird, aber ich würde Ihre PHP-Anwendung laufen mysqlimport auf der CSV-Datei haben. Sicherlich ist es weit über das optimiert, was Sie in PHP machen werden.

+9

Ah der Klassiker "Ich werde dafür downvoted". Die Taktik funktioniert mit mir: +1. – Artefacto

+0

Und ist es möglich, zwei Linien terminiert von? unter Berücksichtigung dieser Karten direkt in Ladedaten In der Datei – Hailwood

3

wird dieser Code gehen Teil Weg durch mich? wird mein Speicher und max Ausführungszeit hoch genug sein?

Warum versuchst du es nicht herauszufinden?

Sie können sowohl den Speicher (memory_limit) als auch die Ausführungszeit (max_execution_time) begrenzen, so dass es kein Problem sein sollte, wenn Sie das wirklich verwenden müssen.

Beachten Sie, dass MySQL eine verzögerte und mehrreihige Einfügen unterstützt:

INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9); 

http://dev.mysql.com/doc/refman/5.1/en/insert.html

0

Sie sollten die Werte akkumulieren und sie alle am Ende oder in Stapeln alle x Datensätze in die Datenbank einfügen. Das Ausführen einer einzigen Abfrage für jede Zeile bedeutet 3,5 Millionen SQL-Abfragen, von denen jede einen beträchtlichen Aufwand mit sich bringt.

Sie sollten dies auch in der Befehlszeile ausführen, wo Sie sich keine Gedanken über die Ausführungszeitgrenzen machen müssen.

Die eigentliche Antwort ist jedoch die Antwort von evilclown, der Import von CSV nach MySQL ist bereits ein gelöstes Problem.

+0

die CSV ist nicht im richtigen Format, es fehlen einige Spalten. – Hailwood

+0

@Hailwood Das sollte kein Problem sein, siehe die Beispiele im Handbuch: 'LOAD DATA INFILE 'persondata.txt' INTO TABLE Personendaten (col1, col2, ...);' – deceze

+0

Würde es Ihnen etwas ausmachen, mir zu sagen, was ich brauche? schreiben? der CSV ist im Format von Code (Neuer-Zeile) Code (Neuer-Zeile) Code (Neue-Zeile) Code (Neue-Zeile) aber ich brauche im Format von ('', Code einzufügen , 0) – Hailwood

0

Ich hoffe, dass kein Webclient auf eine Antwort darauf wartet. Abgesehen davon, dass ich das bereits erwähnte Import-Dienstprogramm aufruft, würde ich das als Job starten und fast sofort Feedback an den Client senden. Lassen Sie die Einfügungsschleife irgendwo aktualisieren, damit der Endbenutzer den Status überprüfen kann, wenn Sie dies unbedingt tun müssen.

0

2 mögliche Wege.

1) Batch den Prozess, dann haben Sie einen geplanten Job importieren Sie die Datei, während Sie einen Status aktualisieren. Auf diese Weise können Sie eine Seite haben, die den Status überprüft und sich selbst aktualisiert, wenn der Status noch nicht 100% ist. Die Benutzer erhalten ein Live-Update, wie viel getan wurde. Dazu müssen Sie jedoch auf das Betriebssystem zugreifen, um die Zeitplanaufgabe einrichten zu können. Und die Task wird inaktiv ausgeführt, wenn nichts zu importieren ist.

2) Lassen Sie die Seite 1000 Zeilen behandeln (oder eine beliebige Anzahl von Zeilen ... Sie entscheiden), dann senden Sie ein Java-Skript an den Browser, um sich mit einem neuen Parameter zu aktualisieren, damit das Skript die nächsten 1000 verarbeiten kann Reihen. Sie können dem Benutzer auch einen Status anzeigen, während dies geschieht. Das einzige Problem ist, dass wenn die Seite irgendwie aktualisiert wird, der Import stoppt.

1
  1. stellen Sie sicher, es gibt keine Indizes auf dem Tisch, als Indizes Einsätze verlangsamen (fügen Sie die Indizes, nachdem Sie alle Einsätze gemacht haben)
  2. anstatt in jedem Aufruf eine neue SQL-Anweisung erstellen der Schleife versuchen und Prepare the SQL Anweisung außerhalb der Schleife, und führen Sie diese vorbereitete Anweisung mit Parametern innerhalb der Schleife. Je nach Datenbank kann das schneller gehen.

Ich habe das oben beim Importieren einer großen Access-Datenbank in Postgres per Perl getan und habe die Einfügezeit auf 30 Sekunden runter. Ich hätte ein Importer-Tool verwendet, aber ich wollte, dass Perl beim Einfügen einige Regeln durchsetzt.

+1

Sie können auch Indizes deaktivieren – Cfreak

Verwandte Themen