# 3 GB Große CSV Datei einlesen und in SQL-DB schreiben



## TJava (31. Aug 2012)

Hallo wie der Titel vermuten lässt möchte ich eine ca. 3Gb große CSV Dateien mit Java(FX) einlesen und in eine MySql-Db schreiben. 

Ich dachte mir bisher das ich diesen Parser verwende:
opencsv - Frequently Asked Questions

Ich frage mich ob das alles so klappt und ob es Probleme geben kann?

Gibt es einen leichteren bzw. anderen Weg CSV-Dateien in eine DB zu schreiben?

Die Dateien werden eine recht simple Datenstruktur haben: 
9 Spalten und halt millionen von Zeilen.

Ich danke euch für eure Erfahrungen und hinweise für evenutelle Probleme die auftreten können.

MfG

TJava


----------



## Marco13 (31. Aug 2012)

Solange man nicht versucht, die Datei am Stück in den Speicher zu lesen, dürfte das kein Problem sein. Wie das mit dem schreiben aussieht ... da kenn' ich mich nicht aus... Das würde man ja nicht Zeilenweise machen, sondern schon mit "chunks" die aus mehreren Zeilen bestehen (... oder? Ist nur geraten...)


----------



## Gast2 (1. Sep 2012)

Moin,



Marco13 hat gesagt.:


> sondern schon mit "chunks" die aus mehreren Zeilen bestehen (... oder? Ist nur geraten...)


nein ist richtig. ZAT (Signatur) ging mal über 2 Stunden, Background ist eine mySQL-DB. Nach dem Umstellung nur noch ca. 5 Minuten. Neben dem Laden aller Daten am Anfang wurde auch am Ende erst alle Ändernung gespeichert. Dazwischen blieb alles drinn.

Lange Rede, kurzer Sinn - die Daten werden am Ende in Blöcken von 1000 Zeilen gespeichert. Ob das Sinn macht, muss aber von DB zu DB entschieden werden. MySQL frisst kaum Speicher, das geht aber nur wenn die Daten gleich auf Pladde gespeichert werden. MsSQL frisst Speicher soviel es geht und den Datenverkehr zur Pladde zu reduzieren (merkt sich die Ergebnisse der letzten Querys). Da kann es sein das es keinen Unterschied macht ob die Datensätze einzeln oder in Chunks ankommen.

hand, mogel


----------



## Lumaraf (1. Sep 2012)

Schau dir auch mal MySQL :: MySQL 5.1 Reference Manual :: 13.2.6 LOAD DATA INFILE Syntax an. Eventuell reichen dir ja schon die Bordmittel von MySQL für die Verarbeitung von CSV-Daten.


----------



## TJava (1. Sep 2012)

Hey Danke das ist schon mal super

Wie sieht es aus mit den Tabellenfeldern, müssen die String sein? 
also bei double und int bekomme ich, nach dem ersten kleinen Testlauf Fehler.

LOAD DATA INFILE 'C:/Dokumente und Einstellungen/Jacky/Desktop/CSV_Projekt.csv' INTO TABLE Lauffeder.csv_import FIELDS TERMINATED BY ';' IGNORE 1 LINES;

Data truncated for column 'Sensor1' at row 1	0.000 sec


----------



## Evil-Devil (4. Sep 2012)

Für deinen Import sollten alle Felder Varchar sein. Ausgehend vom Import kannst du die Daten dann in die jeweiligen Ziel-Tabellen schreiben und ggf. vorher manipulieren.


----------



## TJava (5. Sep 2012)

Danke für den Tipp.

Also meine Tabelle sieht nun so aus:

ID | Timestamp | Sensor1 |Sensor2|...|Sensor8


Ist es auch möglich, die ID halt per Auto Increment setzen zu lassen und die Daten einzutragen?

In der CSV-Datei steht allerding die Idee nicht und so bekomme ich den Fehler das nicht genug Daten enthalten sind um die Zeile zu füllen.

MfG


----------



## TJava (5. Sep 2012)

Hey noch eine Frage gleich hintern dran 

Habe nun eine Klasse erstellt die Connection funktioniert soweit.
Allerdings ist irgendwo ein Standardpfad gesetzt oder sowas, wo SQL die Dateien erwartet.
Wo kann man diese Einstellung bearbeiten?
Verstehe ich nicht da das Statement wenn ich es in der SQL Workbench ausführe auch so funktioniert.



java.sql.SQLException: File 'C:\Dokumente und Einstellungen\All Users\Anwendungsdaten\MySQL\MySQL Server 5.5\Data\Dokumente und EinstellungenJackyDesktopCSV_Projekt.csv' not found (Errcode: 2)




```
try {
            Connection conn = getConnk.getConnection();
            Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
            Statement st = (Statement) conn.createStatement();

            String tableName = "csv_import";
            System.out.println(this.file.getPath());
            //LOAD DATA INFILE 'C:/Dokumente und Einstellungen/Jacky/Desktop/CSV_Projekt.csv' INTO TABLE Lauffeder.csv_import FIELDS TERMINATED BY ';' LINES TERMINATED BY '\n' IGNORE 1 ROWS ;
            st.executeUpdate("LOAD DATA INFILE \"" + this.file.getPath() + "\" INTO TABLE  Lauffeder.csv_import FIELDS TERMINATED BY ';' LINES TERMINATED BY '\n' IGNORE 1 ROWS");
        } catch (Exception e) {
            e.printStackTrace();
        }


        return 0;
```


----------



## Evil-Devil (6. Sep 2012)

Auto ID Anreicherung beim Load Data Import habe ich nie probiert und sollte nach meinem Wissensstand auch nicht funktionieren, da Load Data keine Spaltennamen entgegen nimmt und somit die vollständige Tabellenbreite als Ziel definiert. Also einfach direkt importieren und wenn du dann noch eine Auto ID benötigst musst du die Daten in eine weitere Tabelle übertragen.

Die Quelldatei die eingelesen werden soll kann überall liegen, wichtig ist nur das der Pfad erreichbar ist und etwaige Sonderzeichen escaped übergeben wurden sodass am Ende ein gültiger Pfad bei heraus kommt. Deine Pfad-Angabe sieht auch irgendwie komisch aus. Zweimal Dokumente und Einstellungen, bist du dir sicher das es so korrekt ist?


----------



## TJava (12. Sep 2012)

Hallo, 

die frage ist ja warum der Pfad so ausgegeben wird?

System.out.println(this.file.getPath()) gibt den Pfad richtig aus.


```
st.executeQuery("LOAD DATA LOCAL INFILE \"" + this.file.getAbsolutePath() + "\" INTO TABLE  Lauffeder.csv_import FIELDS TERMINATED BY ';' LINES TERMINATED BY '\n' IGNORE 1 ROWS");
```

Das liefer das gleiche ergebnis aus irgendeinem Grund werden alle \ aus dem Pfad gelöscht.


----------



## Aiwendil (12. Sep 2012)

Musst du die Datei unbedingt per Java laden? Wenn nicht geh doch über die sql-Konsole und gib die Query direkt ein.

die kriegst du so:
Eingabeaufforderung:

```
%MYSQL_SERVER%/bin>mysql -u username [-p]
```
mit username beim mysql server anmelden (falls ein passwort gesetzt ist muss du noch -p hinzufügen, nach dem enter drücken kannst du dann das PW eingeben)


----------



## TJava (12. Sep 2012)

```
String filepath = this.file.getAbsolutePath();
            filepath = filepath.replace("\\", "\\\\");
```

Wenn ich dann folgendes mache läuft es.


```
st.executeQuery("LOAD DATA LOCAL INFILE \"" + filepath + "\" INTO TABLE  Lauffeder.csv_import FIELDS TERMINATED BY ';' LINES TERMINATED BY '\n' IGNORE 1 ROWS");
```

Danke für die Hilfe. Bis demnächst


----------



## Gast2 (13. Sep 2012)

TJava hat gesagt.:


> aus irgendeinem Grund werden alle \ aus dem Pfad gelöscht.



wenn Du Backslash behalten willst, dann musst du die auch escapen


```
st.executeQuery("LOAD DATA LOCAL INFILE \"" + this.file.getAbsolutePath() + "\" INTO TABLE  Lauffeder.csv_import FIELDS TERMINATED BY ';' LINES TERMINATED BY '\\n' IGNORE 1 ROWS");
```

interessanter weise machst Du genau das bei deinem replace


----------

