CSV Import - Dynamisches SQL Statement

mbolder

Mitglied
Hallo zusammen,

ich möchte ein CSV - File mittels Java importieren (habe hierfür Apache commons csv benutzt) und dann in einer Datenbank speichern (Datenbank mittels postgresql.jar verbunden) soweit so gut. Allerdings hat das CSV - File leere Felder, die bei einem fixen SQL Insert Statement von Postgre abgewiesen werden. Meine Idee war nun anhand der Header/Value Paare jeweils pro Zeile zu überprüfen ob Feld leer ist und dann aus den vollen Feldern jeweils ein SQL Insert Statement zu generieren. Sprich "INSERT INTO DATENBANK (?,?,?) VALUES (?,?,?);",("Header1", "Header2", "Header4"), (Value1, Value2, Value4);. Ich verstehe das ein derartiges Vorgehen zu SQL Injektion führen kann, allerdings ist dies ein geschlossenes System, bei dem diese Gefahr ausgeschlossen werden kann.

In Python hätte ich folgendes benutzt, allerdings konnte ich in Java nicht die passenden Äquivalente finden :

Beste Grüße

Markus

Python:
with codecs.open(filename, 'rb', encoding='utf-8') as csvfile:
        reader = csv.DictReader(csvfile, delimiter='\t')
        col=[]
        val=[]
        for row in reader:
              for column, value in row.items():
                    if value != '':
                       col.append(column)
                       val.append(unicode(value, "utf8"))
                        try:
                            query1=sql.SQL("INSERT INTO DATABSE ({}) VALUES ({}) ON CONFLICT DO NOTHING").format(sql.SQL(', ').join(map(sql.Identifier, col)),sql.SQL(', ').join(sql.Placeholder() * len(col)))
                            c.execute(query1, tuple(val),)
                        except psycopg2.IntegrityError as e:
                            print e
                        col=[]
                        val=[]                          
                    conn.commit()
 

mrBrown

Super-Moderator
Mitarbeiter
Allerdings hat das CSV - File leere Felder, die bei einem fixen SQL Insert Statement von Postgre abgewiesen werden.
Warum werden die denn abgewiesen? Lässt die Datenbank die null-Werte nicht zu? Dann sollte das bei der anderen Variante auch scheitern.


Sind die Header vorher bekannt oder sind das beliebige, es gibt aber immer eine Tabelle, die genau diesen Headern entspricht?
 

mbolder

Mitglied
Warum werden die denn abgewiesen? Lässt die Datenbank die null-Werte nicht zu? Dann sollte das bei der anderen Variante auch scheitern.
Postgresql lässt empty Strings bei Feldern die z.b. ein Datum erwarten nicht zu. Bei der Python Variante werden nur die Felder gefüllt, die einen Wert haben, somit tritt das Problem nicht auf. ( if value != '': // filtert die empty strings)

Sind die Header vorher bekannt oder sind das beliebige, es gibt aber immer eine Tabelle, die genau diesen Headern entspricht?

Die Tabelle entspricht der gesamten Anzahl der Header im CSV File, sprich Header1 im CSV-File ist der Name der ersten Column in Postgres etc.
 
Zuletzt bearbeitet von einem Moderator:

mrBrown

Super-Moderator
Mitarbeiter
Postgresql lässt empty Strings bei Feldern die z.b. ein Datum erwarten nicht zu. Bei der Python Variante werden nur die Felder gefüllt, die einen Wert haben, somit tritt das Problem nicht auf. ( if value != '': // filtert die empty strings)
Dann sind die Felder nullable? Ersetz leere Strings einfach mit null.
 

mbolder

Mitglied
Dann sind die Felder nullable? Ersetz leere Strings einfach mit null.


Also wie würde ich den Empty string finden, ich vermisse die Möglichkeit über die Header / Value Paare zu schleifen :

public static void importcsv() {
BufferedReader br = null;
String csvFile = "AMA116.txt";
try {
FileReader in = new FileReader(csvFile);
Iterable<CSVRecord> records = CSVFormat.TDF.withFirstRecordAsHeader().parse(in);
for (CSVRecord record : records) {
 

mbolder

Mitglied
Wie sieht denn dein jetziger Code aus? So wie es oben klingt, hast du ja schon irgendeine Version?

Die hatte ich bereits wieder verworfen, da das hard coden nicht funktioniert hatte.
Hier die vereinfachte Version, im original sind es 45 Columns mit sehr langen Bezeichnungen.....

Statement s = connection.createStatement();
s.executeUpdate('INSERT INTO DATABASE ("Column1", "Column2") VALUES (+record.get(Column1)+,+record.get(Column2)+)');
 

mrBrown

Super-Moderator
Mitarbeiter
Zumindest das "Leerer String Problem" hätte ein toNullOrNonEmpty(record.get(Column1)) (mit entsprechend ausprogrammierter Methode) gelöst.


Wenn du den String automatisch generieren willst, brauchst du nicht Header und Value zusammen.
Aus den Headern kannst du dir ein PreparedStatement generieren lassen, dieses füllst du dann nur noch mit den Werten (und dabei wieder "" mit null ersetzen) und führst es aus.
 

mbolder

Mitglied
Zusätzlich habe ich eine dynamische Routine gefunden! Allerdings habe ich hier noch ein Problem. Die erzeugte Query muss "" um die Table names aufweisen, da die Namen mit einem "-" Zeichen verbunden sind. Bekomme die " jedoch noch nicht am Anfang des ersten sowie Ende des letzten Tables in der Query...

Der Output der Query sieht wie folgt aus.

Query: INSERT INTO DATABASE(settlement-id","settlement-start-date","settlement-end-date) VALUES (?,?,?)
Batch entry 0 INSERT INTO DATABASE(settlement-id","settlement-start-date","settlement-end-date) VALUES('10327656702','2018-06-01T14:02:43+00:00','2018-06-11T09:55:50+00:00')


Java:
package MainFrame;


import java.io.FileNotFoundException;
import java.io.FileReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Date;

import org.apache.commons.lang.StringUtils;

import au.com.bytecode.opencsv.CSVReader;

/**
 *
 * @author viralpatel.net
 *
 */
public class CSVLoader {

    private static final
    String SQL_INSERT = "INSERT INTO ${table}(${keys}) VALUES(${values})";
    private static final String TABLE_REGEX = "\\$\\{table\\}";
    private static final String KEYS_REGEX = "\\$\\{keys\\}";
    private static final String VALUES_REGEX = "\\$\\{values\\}";

    private Connection connection;
    private char seprator;

    /**
    * Public constructor to build CSVLoader object with
    * Connection details. The connection is closed on success
    * or failure.
    * @param connection
    */
    public CSVLoader(Connection connection) {
        this.connection = connection;
        //Set default separator
        this.seprator = ',';
    }
 
    /**
    * Parse CSV file using OpenCSV library and load in
    * given database table.
    * @param csvFile Input CSV file
    * @param tableName Database table name to import data
    * @param truncateBeforeLoad Truncate the table before inserting
    *             new records.
    * @throws Exception
    */
    public void loadCSV(String csvFile, String tableName,
            boolean truncateBeforeLoad) throws Exception {

        CSVReader csvReader = null;
        if(null == this.connection) {
            throw new Exception("Not a valid connection.");
        }
        try {
        
            csvReader = new CSVReader(new FileReader(csvFile), this.seprator);

        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("Error occured while executing file. "
                    + e.getMessage());
        }

        String[] headerRow = csvReader.readNext();

        if (null == headerRow) {
            throw new FileNotFoundException(
                    "No columns defined in given CSV file." +
                    "Please check the CSV file format.");
        }

        String questionmarks = StringUtils.repeat("?,", headerRow.length);
        questionmarks = (String) questionmarks.subSequence(0, questionmarks.length() - 1);

        String query = SQL_INSERT.replaceFirst(TABLE_REGEX, tableName);
        query = query.replaceFirst(KEYS_REGEX, StringUtils.join(headerRow, "\",\""));
        query = query.replaceFirst(VALUES_REGEX, questionmarks);

        System.out.println("Query: " + query);

        String[] nextLine;
        Connection con = null;
        PreparedStatement ps = null;
        try {
            con = this.connection;
            con.setAutoCommit(false);
            ps = con.prepareStatement(query);

            if(truncateBeforeLoad) {
                //delete data from table before loading csv
                con.createStatement().execute("DELETE FROM " + tableName);
            }

            final int batchSize = 1000;
            int count = 0;
            Date date = null;
            while ((nextLine = csvReader.readNext()) != null) {

                if (null != nextLine) {
                    int index = 1;
                    for (String string : nextLine) {
                        date = DateUtil.convertToDate(string);
                        if (null != date) {
                            ps.setDate(index++, new java.sql.Date(date
                                    .getTime()));
                        } else {
                            ps.setString(index++, string);
                        }
                    }
                    ps.addBatch();
                }
                if (++count % batchSize == 0) {
                    ps.executeBatch();
                }
            }
            ps.executeBatch(); // insert remaining records
            con.commit();
        } catch (Exception e) {
            con.rollback();
            e.printStackTrace();
            throw new Exception(
                    "Error occured while loading data from file to database."
                            + e.getMessage());
        } finally {
            if (null != ps)
                ps.close();
            if (null != con)
                con.close();

            csvReader.close();
        }
    }

    public char getSeprator() {
        return seprator;
    }

    public void setSeprator(char seprator) {
        this.seprator = seprator;
    }

}
 
Zuletzt bearbeitet von einem Moderator:

mrBrown

Super-Moderator
Mitarbeiter
Füg in headerRow einfach jedem String vorn und hinten ein Anführungszeichen hinzu, joinen musst du dann nur noch mit dem Komma.
 

mbolder

Mitglied
Füg in headerRow einfach jedem String vorn und hinten ein Anführungszeichen hinzu, joinen musst du dann nur noch mit dem Komma.

Jetzt funktioniert es !! Danke für die Hilfe.


Java:
package MainFrame;


import java.io.FileNotFoundException;
import java.io.FileReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Date;

import org.apache.commons.lang.StringUtils;

import au.com.bytecode.opencsv.CSVReader;


public class CSVLoader {

    private static final
        String SQL_INSERT = "INSERT INTO ${table}(${keys}) VALUES(${values})";
    private static final String TABLE_REGEX = "\\$\\{table\\}";
    private static final String KEYS_REGEX = "\\$\\{keys\\}";
    private static final String VALUES_REGEX = "\\$\\{values\\}";

    private Connection connection;
    private char seprator;

    /**
    * Public constructor to build CSVLoader object with
    * Connection details. The connection is closed on success
    * or failure.
    * @param connection
    */
    public CSVLoader(Connection connection) {
        this.connection = connection;
        //Set default separator
        this.seprator = ',';
    }
  
    /**
    * Parse CSV file using OpenCSV library and load in
    * given database table.
    * @param csvFile Input CSV file
    * @param tableName Database table name to import data
    * @param truncateBeforeLoad Truncate the table before inserting
    *             new records.
    * @throws Exception
    */
    public void loadCSV(String csvFile, String tableName,
            boolean truncateBeforeLoad) throws Exception {

        CSVReader csvReader = null;
        if(null == this.connection) {
            throw new Exception("Not a valid connection.");
        }
        try {
          
            csvReader = new CSVReader(new FileReader(csvFile), this.seprator);

        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("Error occured while executing file. "
                    + e.getMessage());
        }

        String[] headerRow = csvReader.readNext();
              
              

        if (null == headerRow) {
            throw new FileNotFoundException(
                    "No columns defined in given CSV file." +
                    "Please check the CSV file format.");
        }

        String questionmarks = StringUtils.repeat("?,", headerRow.length);
        questionmarks = (String) questionmarks.subSequence(0, questionmarks.length() - 1);

        String query = SQL_INSERT.replaceFirst(TABLE_REGEX, tableName);
        query = query.replaceFirst(KEYS_REGEX, "\""+StringUtils.join(headerRow, "\",\"")+"\"");
        query = query.replaceFirst(VALUES_REGEX, questionmarks);

        System.out.println("Query: " + query);

        String[] nextLine;
        Connection con = null;
        PreparedStatement ps = null;
        try {
            con = this.connection;
            con.setAutoCommit(false);
            ps = con.prepareStatement(query);

            if(truncateBeforeLoad) {
                //delete data from table before loading csv
                con.createStatement().execute("DELETE FROM " + tableName);
            }

            final int batchSize = 1000;
            int count = 0;
            Date date = null;
            while ((nextLine = csvReader.readNext()) != null) {

                if (null != nextLine) {
                    int index = 1;
                    for (String string : nextLine) {
                        date = DateUtil.convertToDate(string);
                        if (null != date) {
                            ps.setDate(index++, new java.sql.Date(date
                                    .getTime()));
                        } else {
                            ps.setString(index++, string);
                        }
                    }
                    ps.addBatch();
                }
                if (++count % batchSize == 0) {
                    ps.executeBatch();
                }
            }
            ps.executeBatch(); // insert remaining records
            con.commit();
        } catch (Exception e) {
            con.rollback();
            e.printStackTrace();
            throw new Exception(
                    "Error occured while loading data from file to database."
                            + e.getMessage());
        } finally {
            if (null != ps)
                ps.close();
            if (null != con)
                con.close();

            csvReader.close();
        }
    }

    public char getSeprator() {
        return seprator;
    }

    public void setSeprator(char seprator) {
        this.seprator = seprator;
    }

}
 

mrBrown

Super-Moderator
Mitarbeiter
Bevor ich dich falsch verstehe: Vorher gab es alle Möglichen Datentypen in der DB (integer, date, etc ...), und weil das Probleme gab, ist jetzt einfach alles nur noch VarChar?
 

mbolder

Mitglied
Bevor ich dich falsch verstehe: Vorher gab es alle Möglichen Datentypen in der DB (integer, date, etc ...), und weil das Probleme gab, ist jetzt einfach alles nur noch VarChar?

Nein, nach tieferer Analyse trat das Problem nur bei drei Feldern auf, zwei integer und ein float Wert. (Alle anderen Felder werden vorher angepasst (siehe Date) oder werden als empty String durch Postgres akzeptiert.) Von diesen Feldern werden nur zwei weiter verwendet (für gelegentliche Berechnungen). Bei 45 Feldern benötigt die Überprüfung ob leer oder nicht in meinem Program deutlich länger, im Vergleich zum nachträglichen recast bei Berechnung mit dem benötigten Float bzw. Integer Feld auf dem Server. Mittlerweile handelt es sich um 251 CSV-Dateien mit zusammen ca. 125 GB, bei deren Verarbeitung dieser "hässliche" Trick eine deutliche Zeitersparnis einbringt. Von der ersten Python Version mit ca. 22 Minuten, über das Java-Equivalent ca. 20 Minuten hin zur "VarChar" Variante ca.13 Minuten (inkl. Recast).
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
baykara MySQL CSV IMPORT Datenbankprogrammierung 6
C Über Classpath MongoDB Treiber einbinden und korrekte import Pfade Datenbankprogrammierung 8
M MySQL MySQL DATETIME-Import Datenbankprogrammierung 9
A Problem mit dem Import und Export von Bildern Datenbankprogrammierung 4
O import in die Datenbank mit Threading Datenbankprogrammierung 3
T Datei import und Datumsformat Datenbankprogrammierung 4
O MySQL Xampp dump-import in Java Datenbankprogrammierung 9
A The import javax.persistence cannot be resolved Datenbankprogrammierung 1
G Import einer csv-Datei in eine H2-Database Datenbankprogrammierung 12
L HSQLDB sichern (import und export von DB) Datenbankprogrammierung 3
G import Oracle.jdbc*; Datenbankprogrammierung 6
A DTS Export/Import MS-SQL Server 2000 Datenbankprogrammierung 3
T SQL-Statement Datenbank nach SQL Statement schließen? Datenbankprogrammierung 7
Kotelettklopfer SQLite Verhindern von doppelter Statement Ausführung Datenbankprogrammierung 25
L SQL-Statement SQL Statement doppelte Einträge finden Datenbankprogrammierung 9
C MySQL SQL Statement wir nicht ausgeführt Datenbankprogrammierung 11
G Datenbank Statement Datenbankprogrammierung 22
Dimax Erstellen ResultSet und Statement Datenbankprogrammierung 30
B MySQL Umkreissuche - brauche Hilfe bei SQL Statement Datenbankprogrammierung 12
D Wiederverwendung / Recycling / Queuing von Statement Objekten Datenbankprogrammierung 1
D SQL Statement gesucht... Datenbankprogrammierung 15
J JDBC SQL Statement mit Parameter Datenbankprogrammierung 7
F Dynamischen String in SQL Statement einbinden Datenbankprogrammierung 9
L SQL Statement mit Switch-Case funktioniert nicht Datenbankprogrammierung 6
U PostgreSQL SELECT Statement Datenbankprogrammierung 5
A Problem mit Create-Statement Datenbankprogrammierung 9
LadyMilka MySQL Syntaxfehler im Statement Datenbankprogrammierung 3
A JDBC Prepared Statement Autoincrement Datenbankprogrammierung 3
P SQL-Statement Bei meinem Statement ist ein Fehler? Datenbankprogrammierung 2
P sql statement alter table foreign key Datenbankprogrammierung 4
T sql Statement Datenbankprogrammierung 9
B JDBC MySQL Statement Datenbankprogrammierung 3
A Sql Statement - Alle Zahlen größer 9 Datenbankprogrammierung 3
F Sql Statement Datenbankprogrammierung 12
A SQL-Statement prüfen Datenbankprogrammierung 3
W Statement als Klassenvariable Datenbankprogrammierung 27
I Select-Statement optimieren Datenbankprogrammierung 14
D SQLite Statement nimmt keine Namen aus getter-Methoden Datenbankprogrammierung 11
alex_fairytail MySQL SQL Statement Delete zwischen Datum1 und Datum2 Datenbankprogrammierung 5
M Access Update Statement Fehler update -> unmöglich? Datenbankprogrammierung 3
R Derby/JavaDB LIKE Statement mit Wildcard Datenbankprogrammierung 20
N Update Prepared Statement Fehler bei Argumenten Datenbankprogrammierung 3
R Select Statement als temporärer Table Datenbankprogrammierung 7
R Derby/JavaDB Select Statement Where bedingung will nicht ganz! Datenbankprogrammierung 4
R Derby/JavaDB Select TOP Statement geht nicht Datenbankprogrammierung 3
S Null Pointer exception statement Datenbankprogrammierung 8
R Derby/JavaDB Insert Statement Probleme Datenbankprogrammierung 14
K statement.execute liefert false Datenbankprogrammierung 6
C PostgreSQL Prepared Statement Datenbankprogrammierung 7
N Prepared Statement mit unbekannter Anzahl von Where-Clauses Datenbankprogrammierung 30
S SQL Statement: executeUpdate Datenbankprogrammierung 15
F wie funktioniert if Statement in SELECT? Datenbankprogrammierung 2
T MySQL PreparedStatement mit INSERTs langsam, Batch-Statement auch Datenbankprogrammierung 4
M MySQL größere von zwei Zahlen in Update Statement Datenbankprogrammierung 2
J [Hibernate] Select Statement Datenbankprogrammierung 4
S HSQLDB DELETE-Statement funktioniert nicht Datenbankprogrammierung 4
L MySQL Prepared Statement batch langsamer als bulk insert? Datenbankprogrammierung 10
L MySQL Kann Statement nicht erzeugen Datenbankprogrammierung 5
T Komme mit dem SQL Statement nicht weiter Datenbankprogrammierung 5
D MySQL Client - Server: Client verabschiedet sich sobald ich Variablen im SQL Statement verwende Datenbankprogrammierung 9
F Frage zu Prepared Statement Datenbankprogrammierung 2
T java.sql.SQLException: unexpected end of statement Datenbankprogrammierung 2
J SQL-Statement Datenbankprogrammierung 10
U MSSQL Verbindung steht, aber meckert beim Statement Datenbankprogrammierung 2
S executeBatch result immet mit Statement.SUCCESS_NO_INFO Datenbankprogrammierung 4
G Frage zum Insert-Statement Datenbankprogrammierung 2
J JDBC- Statement Ausführung nicht persistent Datenbankprogrammierung 2
A Connection Variable in anderer Klasse verwenden -> statement Datenbankprogrammierung 2
D Problem: Prepared Statement (Insert) funktioniert nicht. Datenbankprogrammierung 3
G Mit Prepared Statement in MDB schreiben Datenbankprogrammierung 7
Z DELETE-Statement Datenbankprogrammierung 6
W Prepared Statement und Verbindungsverlust Datenbankprogrammierung 4
G INSERT-STATEMENT Datenbankprogrammierung 6
alexpetri hsql statement problem Datenbankprogrammierung 3
W invalid sql statement ? Datenbankprogrammierung 3
S Probleme mit statement.close() Datenbankprogrammierung 10
G Wie ein SQL-Statement zusammensetzen? Datenbankprogrammierung 5
T Select Statement auf Relation Datenbankprogrammierung 3
S Connection/Statement/ResultSet auf einmal geschlossen Datenbankprogrammierung 8
G MsSQL | Statement.execute(String s) Datenbankprogrammierung 3
W Prepared Statement mehrere Zeichenketten Datenbankprogrammierung 4
G Keine Aussicht mit Prepared Statement Datenbankprogrammierung 10
G Für jede SQL-Abfrage eigenes Statement und ResultSet? Datenbankprogrammierung 3
C Statement/Connection SQLWarning Datenbankprogrammierung 4
J sql-statement Datenbankprogrammierung 7
G Update Statement automatisch unterteilen lassen Datenbankprogrammierung 3
P Prepared Statement scheint nicht zu funktionieren Datenbankprogrammierung 14
T SQL-Statement parser? Datenbankprogrammierung 11
N Prepared Statement Datenbankprogrammierung 8
G Problem mit Suchen Statement Java und MS-SQL-Server Datenbankprogrammierung 9
J SELECT-Statement Datenbankprogrammierung 4
S Statement.getGeneratedKeys() funktioniert nicht Datenbankprogrammierung 4
G Problem mit Delete-Statement Datenbankprogrammierung 3
L mehrere Abfragen mit einem Statement!? Datenbankprogrammierung 5
E Problem mit create-Statement Datenbankprogrammierung 5
E SQL-Statement um eine Nummer in der Datenbank zu suchen Datenbankprogrammierung 3

Ähnliche Java Themen

Neue Themen


Oben