CSV in MySQL schreiben 2

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Mittwoch 24. Oktober 2007, 21:49

Einen schönen Guten Abend

ich hoffe es ist nicht schlimm, dass ich gleich diesen Thread missbrauche. Aber geht halt auch genau im csv import in mysql.

Hab ein csv File, eigentlich mit Zahlen und Strings, hab es jetzt zu Beginn allerdings nur mal mit Strings probiert.

Sieht bisher so aus:

[Edit (Leonidas): Code ausgelagert]

Das Ganze bricht mit folgender Fehlermeldung ab:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\<string>", line 49, in ? # in 49 steht das mit dem INSERT TO
  File "C:\Python24\Lib\site-packages\MySQLdb\cursors.py", line 151, in execute
    query = query % db.literal(args)
TypeError: not enough arguments for format string
Ich versteh das nicht ganz. Inwiefern nicht genügend Argumente? Sind genau 5 Spalten und das sieht auch in der csv so aus:

Code: Alles auswählen

# nach Scriptabbruch einfach mal ausgegeben
>>> row
['1;A;1; blablabla ;blublubub']
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Mittwoch 24. Oktober 2007, 22:24

meneliel hat geschrieben:

Code: Alles auswählen

# nach Scriptabbruch einfach mal ausgegeben
>>> row
['1;A;1; blablabla ;blublubub']
Das ist nur eine Spalte. ``row[0]`` gibt einen String, ``row[1]`` gibt es schlichtweg nicht.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Mittwoch 24. Oktober 2007, 22:24

Hallo meneliel!

Ich habe diesen Beitrag vom anderen Topic abgetrennt, da man nicht mehr antworten konnte.

Der Grund dürfte sein, dass du an ``execute`` eine Liste mit **einem** Eintrag verfüttern möchtest.

Code: Alles auswählen

['1;A;1; blablabla ;blublubub']
--> nur ein String.

Ich weiß nicht wie deine CSV-Datei aufgebaut ist, aber da stimmt etwas nicht. Kann es sein, dass jede Zeile zusätzlich noch in Anführungszeichen gesetzt ist?

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Mittwoch 24. Oktober 2007, 22:26

gerold hat geschrieben:Ich habe diesen Beitrag vom anderen Topic abgetrennt, da man nicht mehr antworten konnte.
Komisch, parallel dazu habe ich versucht das gleiche zu tun, aber dann habe ich gemarkt, dass der Server insgesamt sich dumm angestellt hat, also es vermutlich nicht am Thread gelegen hat. Denke ich.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
BlackJack

Mittwoch 24. Oktober 2007, 22:26

Ich tippe mal eher darauf das CSV "*comma* separated values" heisst, die Daten aber offensichtlich durch Semikola getrennt sind. Dass muss man dem Reader auch sagen -> `delimiter`-Argument.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Mittwoch 24. Oktober 2007, 22:34

BlackJack hat geschrieben:Ich tippe mal eher darauf das CSV "*comma* separated values" heisst, die Daten aber offensichtlich durch Semikola getrennt sind. Dass muss man dem Reader auch sagen -> `delimiter`-Argument.
Hallo BlackJack!

Die Strichpunkte sind mir gar nicht aufgefallen. :D

Code: Alles auswählen

>>> from StringIO import StringIO
>>> s = StringIO("""a;b;c
... d;e;f
... g;h;i""")
>>> import csv
>>> for row in csv.reader(s):
...     print row
...     
['a;b;c']
['d;e;f']
['g;h;i']
>>> s = StringIO("""a;b;c
... d;e;f
... g;h;i""")
>>> for row in csv.reader(s, delimiter = ";"):
...     print row
...     
['a', 'b', 'c']
['d', 'e', 'f']
['g', 'h', 'i']
>>> 
lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Donnerstag 25. Oktober 2007, 10:12

Hallo Gerold, Hallo BlackJack,

Danke Euch vielmals. Jetzt funktioniert es natürlich problemlos ...

... Warum seh/denk ich an so was nicht selber? Bei meinen Studis in den Excel-Kursen seh ich doch auch jede fehlende Klammer etc. sofort... :(
midan23
User
Beiträge: 137
Registriert: Sonntag 21. Mai 2006, 21:41
Wohnort: Müchen
Kontaktdaten:

Donnerstag 25. Oktober 2007, 12:02

meneliel hat geschrieben: ... Warum seh/denk ich an so was nicht selber? Bei meinen Studis in den Excel-Kursen seh ich doch auch jede fehlende Klammer etc. sofort... :(
Könnte es daran liegen, das einige Programme seltsame Ansichten haben, was eine CSV-Datei ist ?
Beispiel: Excel 2003 benutzt beim Export als CSV-Datei immer Semikolons als Trennzeichen und ich habs ihm bis jetzt nicht abgewöhnen können ...
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Donnerstag 25. Oktober 2007, 12:38

midan23 hat geschrieben: Könnte es daran liegen, das einige Programme seltsame Ansichten haben, was eine CSV-Datei ist ?
Beispiel: Excel 2003 benutzt beim Export als CSV-Datei immer Semikolons als Trennzeichen und ich habs ihm bis jetzt nicht abgewöhnen können ...
Daran könnte es zumindest liegen, dass da überhaupt semikolons drin sind, hab es ja mit Excel exportiert.
ABER nachdem das alles nicht ging, HABE ich mir ja die Datei mit dem Editor angeguckt, weil dachte schon Excel hat irgendwelche Spalten eingefügt, seltesam trennzeichen (z.B. TAB) gewählt, oder was auch immer. Da hab ich die Semikolons gesehen und war erst mal beruhigt und dachte okay, passt. Hab in dem Moment einfach nicht realisiert, dass ich ja Kommas brauche.

Habe dabei noch eine weitere Frage: ich wurde darauf hingewiesen, dass es problematisch sein könnte für die Performance, da jede Zeile, die ich importiere mit einem INSERT an die Datebank geschickt wird und es besser wäre, ich würde lieber 1 Query mit vielen ()-Value Teilen basteln.

Mit executemany wir da auch INSERT für jede Zeile neu aufgerufen, oder? Nur ich muss mich nicht drum kümmern?

Wie sehr geht das wirklich auf die Performance? Oder kann ich das ignorieren, auch wenn ich am Ende mal 10000 Zeilen haben sollte?
BlackJack

Donnerstag 25. Oktober 2007, 12:43

So wirklich standardisiert sind CSV-Dateien ja nicht und das Semikolon macht bei Excel auch Sinn, weil in vielen "locales" das Komma in irgendeiner Weise in Zahlen vorkommen kann, aber das Semikolon eher nicht. Bei uns wird's als Trenner zwischen Vor- und Nachkommastellen benutzt (3,14), im englischen oft als Trenner zwischen Tausendern (1,000,000.42). Wenn man solche Dateien also mit "dummen" Programmen verarbeiten will oder muss, ist es praktischer ein Trennzeichen zu wählen, das nicht in Zahlen vorkommen kann.
BlackJack

Donnerstag 25. Oktober 2007, 12:55

@meleniel: Das mit dem "grossen" Insert würde ich nicht machen, das macht das Programm nur unnötig unübersichtlich.

Was `executemany()` *wirklich* macht, hängt vom Datenbankmodul und der Datenbank ab. Es kann auf jeden Fall schneller sein als eine eigene Schleife mit 'INSERT'-Anweisungen, es ist nicht langsamer als diese. Ist also auf jeden Fall zu empfehlen.

Eine dumme DB+Modul formatiert die Werte tatsächlich in die Abfrage als Zeichenkette hinein und schickt das dann zur Datenbank, welche die Abfrage parst und verarbeitet. Und das für *jeden* Satz von Werten.

Bei intelligenteren DBs+Modulen wird die SQL-Anweisung *einmal* mit den Platzhaltern in einen internem Abfrageplan der DB übersetzt und dann wird mit dieser vorkompilierten Anfrage und den Werten gearbeitet. Dabei werden die Werte auch nicht erst in Zeichenketten und dann in der DB wieder in etwas anderes gewandelt.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Donnerstag 25. Oktober 2007, 12:55

meneliel hat geschrieben:ich wurde darauf hingewiesen, dass es problematisch sein könnte für die Performance, da jede Zeile, die ich importiere mit einem INSERT an die Datebank geschickt wird und es besser wäre, ich würde lieber 1 Query mit vielen ()-Value Teilen basteln.
Hallo meneliel!

Die größte Bremse wäre, wenn du nach jedem INSERT ein COMMIT machen würdest. Das spürt man deutlich.

Von den meisten Datenbankschnittstellen wird von der Methode ``execute`` die Methode ``executemany`` mit nur einem Datensatz aufgerufen. Also ist der Unterschied *meistens* nur der, dass noch eine Funktion dazwischen ist. Mehr nicht.

Es gibt aber auch Datenbankschnittstellen, die bei ``executemany`` die SQL-Anweisung vom SQL-Server vorkompilieren lassen (=Prepare). Und dann eigentlich nur mehr die Daten nachschicken. Das könnte schon etwas an Geschwindigkeit bringen. -- Aber sicher nicht viel.

Daumenregel: 1000 (einfache) Datensätze pro Sekunde (rein oder raus) sind normal. Wenn also dein IMPORT von 10000 Datensätzen viel länger als 10 Sekunden dauert, dann kann man wahrscheinlich optimieren.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
kbrust
User
Beiträge: 11
Registriert: Montag 9. Oktober 2006, 11:53
Kontaktdaten:

Donnerstag 25. Oktober 2007, 12:59

Je nachdem für was die Datenbank sonst noch zuständig ist (gut ausgelasteter Webserver?), kann das auf jeden Fall auf die Performance schlagen. Bei einem 10.000 Zeilen import würde ich normalerweise schon anfangen zu planen, sprich sowas dann spät nachts einspielen.

Aber:

Muss es denn unbeding CSV->Python->MySQL sein? MySQL bringt ein nettes Statement mit: "load data infile", welches direkt aus CSV-Dateien lesen kann, sogar mit Wahlmöglichkeit für den Field-Separator.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Donnerstag 25. Oktober 2007, 12:59

@BlackJack:

Gestern und heute überschneiden sich unsere Beiträge (zeitmäßig) schon ziemlich auffällig. :lol:

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Donnerstag 25. Oktober 2007, 13:14

kbrust hat geschrieben: Muss es denn unbeding CSV->Python->MySQL sein? MySQL bringt ein nettes Statement mit: "load data infile", welches direkt aus CSV-Dateien lesen kann, sogar mit Wahlmöglichkeit für den Field-Separator.
Es muss am Ende anwenderfreundlich sein. Da da später weitere Tabelllen befüllt werden und die Daten z.T direkte Berechnungen sind, die ich aus dem GIS bekomme, erscheint es mir irgendwie "einfacher" das alles Python aufzurufen.

Da müssen am Ende halt nur ein paar Scripte gestartet werden.

*überleg* Aber dann müsste ich doch auch mit execute "load data infile" aufrufen können?

EDIT:
* mal probieren bin
Antworten