Tabellen aus CSV Dateien in SQLite Datenbanken importieren

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
BlackJack

@ThoSchoo: Ich wüsste nicht wie man das noch einfacher demonstrieren könnte warum das nicht funktioniert. Du versuchst im Programm auf einen Schlüssel zuzugreifen den es in dem Wörterbuch nicht gibt (→ `KeyError`) weil die Schreibweise in der CSV-Datei eine andere ist und `dict` nun mal auf Gleichheit testet wenn man über einen Schlüssel zugreift.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@ThoSchoo: und was hatten wir gerade über unsinnige Abkürzungen? Liebe-Grüße-Orte scheint was romantisches zu sein, dafür eine ID zu vergeben eher weniger :D .
ThoSchoo
User
Beiträge: 20
Registriert: Freitag 8. Januar 2016, 12:54

@BlackJack
Ahh nun check ich es .. Sorry für das Brett vor meinem Kopf :D
Aber wie kann ich dieses Problem umgehen bzw. es lösen ?
Üpsilon
User
Beiträge: 222
Registriert: Samstag 15. September 2012, 19:23

Na indem du den Spaltennamen richtig schreibst :wink: also LGORT_ID statt Lgort_Id
PS: Die angebotene Summe ist beachtlich.
ThoSchoo
User
Beiträge: 20
Registriert: Freitag 8. Januar 2016, 12:54

@Üpsilon

das ändert leider nichts ... der Fehler bleibt der selbe
BlackJack

@ThoSchoo: Das kann nicht sein.
ThoSchoo
User
Beiträge: 20
Registriert: Freitag 8. Januar 2016, 12:54

Code: Alles auswählen

import csv
import sqlite3


con = sqlite3.connect("LGORT")
cur = con.cursor()
cur.execute("CREATE TABLE invoice (LGORT_ID INTEGER, City TEXT, WERKS_ID INTEGER);")

with open('tbl_LGORT.csv') as input_file:
        reader = csv.DictReader(input_file)
        to_db = [(i["LGORT_ID"], i["City"], i["WERKS_ID"]) for i in reader]

cur.executemany("Insert into invoice (LGORT_ID, City, WERKS_ID) Values (?, ?, ?);", to_db)
con.commit()
to_db = [(i["LGORT_ID"], i["City"], i["WERKS_ID"]) for i in reader]
KeyError: 'LGORT_ID'

Leider doch ..
Zuletzt geändert von Anonymous am Dienstag 12. Januar 2016, 00:10, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@ThoSchoo:

1) Setz bitte den Code in Zukunft in CodeBox-Tags. Direkt über dem Eingabefenster ist eine Dropdown-Box ("Code auswählen"). Da drauf klicken, die Buchstaben p und y eingeben und Enter drücken, schon hast du ein Paar CodeBox-Tags. Da kommt der Code rein.

2) Kann es sein, dass in deiner Datei tbl_LGORT.csv in der ersten Zeile etwas anderes steht, als LGORT_ID etc? Stehen da überhaupt Spaltennamen? Oder gleich in der ersten Zeile ein Datensatz? Falls letzteres, musst du die gewünschten Spaltennamen beim Aufruf von csv.DictReader() als Argument (zB. in einer Liste) übergeben. Wie das genau geht, steht in der Doku.
In specifications, Murphy's Law supersedes Ohm's.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@ThoSchoo: laut Deinem Beispiel müßte der delimiter ';' sein.
ThoSchoo
User
Beiträge: 20
Registriert: Freitag 8. Januar 2016, 12:54

@Sirius3
JAAA.. das scheint das Problem gewesen zu sein ..

Code: Alles auswählen

con = sqlite3.connect("LGORT")
cur = con.cursor()
cur.execute("CREATE TABLE invoice (LGORT_ID INTEGER, City TEXT, WERKS_ID INTEGER);")

with open('tbl_LGORT.csv') as input_file:
        reader = csv.DictReader(input_file, delimiter = ";")
        to_db = [(i["LGORT_ID"], i["City"], i["WERKS_ID"]) for i in reader]

cur.executemany("Insert into invoice (LGORT_ID, City, WERKS_ID) Values (?, ?, ?);", to_db)
con.commit()
Nun gibt er mir allerdings beim auslesen ein "u" vor der City !?
  • (1, u'Bramsche', 1)
    (2, u'Bramsche', 1)
    (3, u'Bramsche', 1)
BlackJack

@ThoSchoo: Das macht nichts.
ThoSchoo
User
Beiträge: 20
Registriert: Freitag 8. Januar 2016, 12:54

Wie könnte ich denn nun das Programm variabler machen, so dass es die Spaltennamen eigenständig erkennt ?
BlackJack

@ThoSchoo: Die Spaltennamen der CSV-Datei kann man vom `DictReader`-Objekt abfragen. Die Spaltennamen einer Datenbanktabelle kann man über das `description`-Attribut auf Cursor-Objekten abfragen wenn man eine Abfrage aller Spalten einer Tabelle macht. Datenbankspezifisch gibt es in der Regel noch andere Wege an diese Information zu kommen, ohne eine Abfrage der Daten selbst zu machen. An der Stelle würde ich dann aber auf eine Abstraktion wie SQLAlchemy ausweichen.

Datenbankspalten würde ich übrigens in Kleinbuchstaben bennenen. Eigentlich sollten die „case insensitive“ sein, allerdings machen das nicht alle Datenbanken so oder lassen sich zumindest auch anders konfigurieren. Genau wie bei Bezeichnern in einem Programm sollte man sich auch auf eine natürliche Sprache beschränken und nicht eine Spalte in deutsch und eine andere in englisch benennen. Da kommt man schnell durcheinander.
ThoSchoo
User
Beiträge: 20
Registriert: Freitag 8. Januar 2016, 12:54

@BlackJack Danke aber ich hab leider nicht sehr viel verstanden, von dem was du mir mitteilen wolltest :D


Da es mehrere CSV Dateien sind, die alle in einem Verzeichnis liegen und alle als einzelne Tabellen in eine Datenbank eingetragen werden sollen, habe ich mal das hier zusammengesucht und -gesetzt:

Code: Alles auswählen

import csv
import sqlite3
import os
import glob



db = ("Mockupdata")
con = sqlite3.connect(db)
cur = con.cursor()

# traverse the directory and process each .csv file
for csvfile in glob.glob(os.getcwd("", "*.csv")):
    # remove the path and extension and use what's left as a table name
    tablename = os.path.splitext(os.path.basename(csvfile))[0]

    with open(csvfile, "rb") as inputfile:
        reader = csv.reader(inputfile)
 
        header = True
        for row in reader:
            if header:
                # gather column names from the first row of the csv
                header = False
 
                sql = "DROP TABLE IF EXISTS %s" % tablename
                cur.execute(sql)
                sql = "CREATE TABLE %s (%s)" % (tablename,
                          ", ".join([ "%s text" % column for column in row ]))
                cur.execute(sql)
                       
                insertsql = "INSERT INTO %s VALUES (%s)" % (tablename,
                            ", ".join([ "?" for column in row ]))
 
                rowlen = len(row)
            else:
                # skip lines that don't have the right number of columns
                if len(row) == rowlen:
                    cur.execute(insertsql, row)
 
        con.commit()
 
cur.close()
con.close()
Hier scheitert es allerdings schon bei dem durchsuchen des Ordners :

Code: Alles auswählen

    for csvfile in glob.glob(os.getcwd("", "*.csv")):
TypeError: getcwd() takes no arguments (2 given)
Ich arbeite gerade auf einem Mac .. das Ganze soll allerdings am besten überall anwendbar sein
Zuletzt geändert von Anonymous am Dienstag 12. Januar 2016, 18:47, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Schau dir mal die Fehlermeldung an und dann die Klammern in der betreffenden Zeile.
BlackJack

@ThoSchoo: Der `os.getcwd()`-Aufruf ist hier sowieso überflüssig.

Das Programm würde ja nun sämtliche Felder in der Datenbank vom Typ TEXT anlegen. Und Constraints wie NOT NULL oder PRIMARY KEY gehen auf diese Art und Weise ja auch nicht.

Warum gibt es in der Datei überhaupt Zeilen mit der falschen Länge?

Statt *in* der Schleife ständig zu prüfen ob man die erste Zeile vor sich hat oder nicht, würde man die erste Zeilen einfach *vor* der Schleife mit `next()` vom Reader-Objekt abfragen.

Das ganze ist mittlerweile komplex genug um es sinnvoll auf Funktionen aufzuteilen.

Bei der Gelegenheit kann man dann auch gleich das Hauptprogramm in eine Funktion stecken (üblicherweise `main()` genannt) und auf Modulebene damit dann keinen Code mehr haben der nicht Konstanten, Funktionen, oder Klassen definiert.
ThoSchoo
User
Beiträge: 20
Registriert: Freitag 8. Januar 2016, 12:54

@cofi

wenn ich es so schreibe :

Code: Alles auswählen

csvfile in glob.glob(os.getcwd(""), "*.csv"):
gibt er mir das :

Code: Alles auswählen

 for csvfile in glob.glob(os.getcwd(""), "*.csv"):
TypeError: getcwd() takes no arguments (1 given)
ThoSchoo
User
Beiträge: 20
Registriert: Freitag 8. Januar 2016, 12:54

@BlackJack

mit dem `os.getcwd()` will ich ja mein Arbeitsverzeichnis aufrufen in mein die Dateien vorhanden sind
Wie gelange ich denn sonst in das Verzeichnis? :|

Es dürfte eigentlich keine Daten mit einer falschen länge geben
BlackJack

@ThoSchoo: Die Fehlermeldung ist doch ziemlich deutlich. Die Funktion erwartet keine Argumente, Du übergibst aber ein Argument. Lass das sein.

Zum aktuellen Arbeitsverzeichnis muss man nicht gelangen, da ist man bereits. Das ist ja gerade die Bedeutung von „aktuellem Arbeitsverzeichnis“, das von dort aus alle relativen Pfadangaben gesucht werden.

Wenn es keine Zeilen mit falscher Anzahl von Feldern geben kann, warum ist dann eine Prüfung darauf im Programm?
ThoSchoo
User
Beiträge: 20
Registriert: Freitag 8. Januar 2016, 12:54

So funktioniert es :

Code: Alles auswählen

import csv
import sqlite3
import os
import glob



db = ("Mockupdata")
con = sqlite3.connect(db)
cur = con.cursor()

# traverse the directory and process each .csv file
for csvfile in glob.glob( "*.csv"):
    # remove the path and extension and use what's left as a table name
    tablename = os.path.splitext(os.path.basename(csvfile))[0]

    with open(csvfile, "rb") as inputfile:
        reader = csv.reader(inputfile, delimiter = ";")
 
        header = True
        for row in reader:
            if header:
                # gather column names from the first row of the csv
                header = False
 
                sql = "DROP TABLE IF EXISTS %s" % tablename
                cur.execute(sql)
                sql = "CREATE TABLE %s (%s)" % (tablename,
                          ", ".join([ "%s text" % column for column in row ]))
                cur.execute(sql)
                       
                insertsql = "INSERT INTO %s VALUES (%s)" % (tablename,
                            ", ".join([ "?" for column in row ]))
 
                rowlen = len(row)
 
        con.commit()
 
cur.close()
con.close()
allerdings ist es so alles in TEXT

wie könnte ich das denn umschrieben damit es den jeweiligen DatenTypen erkennt ?
Zuletzt geändert von Anonymous am Dienstag 12. Januar 2016, 20:01, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Antworten