bulk load über java

Hi!

Ich benutze Postgres 8.4 mit einem JDBC4 Treiber und habe eine Frage zum Bulk Load.

Ich suche eine von Java ausführbar Methode, um so etwas wie \copy table(column 1, column 2) from file.csv… auf ein csv File auszuführen.

Ich erstelle aus anderen Daten normalerweise eine csv Datei und lade sie dann per \copy in die Datenbank.
Nun soll das Programm an dem ich grade arbeite auch für Benutzer verständlich sein, die nicht auf Kommandozeilenebene ein \copy ausführen wollen/können. (Ziel: Benutzer wählt auf der GUI ein File aus. Dann wird es in Java verarbeitet und als Ergebnis ist dieses File dann in der Datenbank und das alles ohne, dass der Benutzer csv Files löschen, kopieren oder \copy machen muss)

Nun stelle ich mir 2 Szenarien vor von denen ich nicht weiß wie ich sie effizient (also ohne das Standard INSERT) implementieren kann.

  1. Ich parse meine Daten direkt in einen Buffer und führe aus diesem Buffer (der irgendwie wie ein in Memory csv aussieht (zb. 2000 csv Zeilen)) ein copy Befehl aus. Wie kann ich das machen?

  2. Ich parse meine Daten in ein CSV File, speichere es auf der Platte und führe einen Copy Befehl über Java aus, der mir die Daten in die Datenbank lädt. Für so etwas gibt es tatsächlich den copymanager, der schiebt die csv Daten trotzdem nochmal mit einem Reader durch die JAVA VM und erst dann in die Datenbank. Diesen Schritt möchte ich verhindern, denn es ist absolut ineffizient 10GB Dateien von einem Java Reader durch die VM laufen zu lassen.


    Was denkt Ihr was am effizientesten ist? Und wie kann ich das implementieren? Ich vermute, dass Methode 1 am besten wäre, wenn implementierbar, denn dann brauche ich die CSV gar nicht auf Platte zu schreiben sondern brauche nur einen Read(vom nicht nicht geparsten File) und einem Write(direkt in die db).

Vielen Dank schon mal für die schnellen Antworten.

Meister U.

Du könntest dir mal den SQL-Befehl COPY … FROM STDIN anschauen.

Dann musst du die Daten von Java aus nur noch in die Datenbankverbindung schieben.

schonmal vielen Dank.

Aber wie schiebe ich? Der Befehl würde ja irgendwie so aussehen:

copyManager.copyIn(“COPY foo FROM stdin”);

foo ist eine Tabelle, die nur eine Spalte hat und ich würde nun gerne 1,2,3,4,5… reinschieben. Wie mache ich das?

Wenn es nur darim geht, eine einspaltige Tabelle mit fortlaufenden Zahlen zu füllen, dann geht das trivial einfacher so:

test=# create table schiebe (i int);
CREATE TABLE
test=# insert into schiebe select generate_Series(1,10);
INSERT 0 10
test=# select * from schiebe;
 i
----
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
(10 rows)

Es bleibt nun die Frage, wozu man sowas machen will, wenn es generate_series() gibt. Dies macht so eine Tabelle ganz, ganz sicher überflüssig.


Andreas

Hi!

Ich will mit diesem einfachen Beispiel einfach mal den COPY Operator über Java testen. Sobald ich das verstehe, werden natürlich andere/komplexere Daten eingefügt.

Keine Ahnung, wie man in Java direkt in einen Stream schreibt. Steht aber sicher irgendwo in der Dokumentation.

Wenn du etwas findest, füge doch bitte deine Fundstellen hier ein. Damit andere in Zukunft etwas davon haben.

Es gibt den CopyManager CopyManager

Mit diesem wird eine Methode copyIn anwendbar.

copyIn(sql, stream)
copyIn(sql, Reader)

Ich habe jetzt so “gelöst”:
Connection connection = DriverManager.getConnection(,,*);
PGConnection pConn = (PGConnection)connection;
CopyManager copyManager = pConn.getCopyAPI();
copyManager.copyIn(“COPY bla FROM stdin”, new FileReader(new File(“file.txt”)));

Nun ergeben sich 2 Probleme:

  1. Ich muss immernoch zuerst ein CSV File schreiben und kann nicht wirklich vom Parser direkt in die DB parsen und
  2. bekomme ich jetzt Fehler wie: “COPY-Ende-Markierung verfälscht” oder “ungültige Byte-Sequenz für Kodierung »UTF8«: 0x00
    Hinweis: Dieser Fehler kann auch auftreten, wenn die Bytesequenz nicht mit der Kodierung übereinstimmt, die der Server erwartet, welche durch »client_encoding« bestimmt wird.”
    je nachdem ob ich ein . am Ende mache oder nicht.

Ich vermute, dass ich ein spezielles CharSet mit Java benutzen muss. Oder gibt es weitere Ideen?

Deine Daten passen nicht zu UTF8.

select * from depesz; » Blog Archive » ERROR: invalid byte sequence for encoding

Andreas

eine andere, viel trivialere Frage habe ich dann noch

ich habe

dbtest=# copy bla from ‘bla.txt’;
FEHLER: konnte Datei »bla.txt« nicht zum Lesen öffnen: No such file or directory

aber

dbtest=# \copy bla from bla.txt

das läuft ohne Probleme. Den Befehl \copy kann ich von Java aus aber nicht anschieben, lediglich copy. Wo liegt also, dass Problem, dass ein Befehl die Datei findet, die andere aber nicht?

Bzw warum brauch ich den totalen und nicht den relativen Pfad?

Das eine arbeitet auf dem Server und das andere auf dem Client.

Command:     COPY
Description: copy data between a file and a table


-- versus

Input/Output
  \copy ...              perform SQL COPY with data stream to the client host

Andreas

Du darfst nicht \copy mit COPY verwechseln.

Wieso musst du das in einer Datei zwischenspeichern? Du kannst sicherlich auch direkt in den Stream schreiben, oder?