Datenimport in PostgreSQL datenbank mit Hilfe von Phyton

Code-Stücke können hier veröffentlicht werden.
BoOnOdY
User
Beiträge: 112
Registriert: Samstag 22. Juli 2006, 13:38

Code: Alles auswählen

import csv 
import psycopg2

in_file = open('02.08.2006.csv', 'rb') 
reader = csv.reader(in_file, delimiter=';') 

connection = psycopg2.connect ("dbname=GBC user=Import password=import") 
cursor = connection.cursor() 
cursor.executemany("INSERT INTO call (Datum, Uhrzeit, Eingangsport, Ausgangsport, Eingangsnummer, Ausgangsnummer, Dauer, Fehler) VALUES (?, ?, ?, ?, ?, ?, ?, ?), reader") 
cursor.close() 
connection.commit() 
connection.close() 

in_file.close()


der Fehler is, dass sich mein python aufhängt
und windows sagt "python hat ein problem festgestellt und musste beendet werden"
kann es sein das ich irgend nen falschen Befehl benutzt hab ? müssen die Fragezeichen noch durch was ersetzt werden?
ich habs genau so ausgeführt.

Gruß Tim :)
Ps: der SQL code

Code: Alles auswählen

CREATE TABLE "Call"
(
  "Datum" text NOT NULL,
  "Uhrzeit" text,
  "Eingangsport" int2,
  "Ausgangsport" int2,
  "Eingangsnummer" text,
  "Ausgangsnummer" text,
  "Dauer" int2,
  "Fehler" text
) 
WITHOUT OIDS;
ALTER TABLE "Call" OWNER TO "Tim";
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

BoOnOdY hat geschrieben:kann es sein das ich irgend nen falschen Befehl benutzt hab ? müssen die Fragezeichen noch durch was ersetzt werden?
ich habs genau so ausgeführt.
[...]

Code: Alles auswählen

CREATE TABLE "Call"
(
  "Datum" text NOT NULL,
  "Uhrzeit" text,
  "Eingangsport" int2,
  "Ausgangsport" int2,
  "Eingangsnummer" text,
  "Ausgangsnummer" text,
  "Dauer" int2,
  "Fehler" text
) 
WITHOUT OIDS;
ALTER TABLE "Call" OWNER TO "Tim";
Hi Tim!

- Erstelle dir immer einen Primärschlüssel. (im Beispiel "id")
- int2 würde ich auf int4 erhöhen.
- text ??? --> varchar(maximale Feldgröße)
- Bei leeren Feldern, statt einem leeren String None übergeben (= vielleicht besser)
- Bei *psycopg2* ist ``%s`` als Platzhalter bei ``execute()`` und ``executemany()`` einzusetzen

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

#CREATE TABLE call (
#   id serial PRIMARY KEY,
#   datum date, 
#   uhrzeit time, 
#   eingangsport int4, 
#   ausgangsport int4, 
#   eingangsnummer varchar(50), 
#   ausgangsnummer varchar(50), 
#   dauer int4, 
#   fehler varchar(50)
#) WITHOUT OIDS;

import psycopg2
import csv
import datetime


# Datenbank-Connection und -Cursor erstellen
conn = psycopg2.connect(user = "Import", password = "import", database = "GBC")
cur = conn.cursor()

# CSV-Datei öffnen
f = file("02.08.2006.csv", "r")

# CSV-Datei dem Parser übergeben
csvdata = csv.reader(f, delimiter = ';')

# Jede, nicht leere, Zeile durchlaufen
for row in ( row for row in csvdata if row ):
    #
    # Daten vorbereiten (in den korrekten Datentyp umwandeln)
    #
    data = []
    # Datum
    d, m, y = ( int(val) for val in row[0].split(".") )
    data.append(datetime.date(y, m, d))
    # Uhrzeit
    H, M, S, ms = ( int(val) for val in row[1].split(":") )
    data.append(datetime.time(H, M, S, ms))
    # Eingangsport und Ausgangsport, Eingangsnummer und Ausgangsnummer
    data += [int(row[2]), int(row[3]), row[4] or None, row[5] or None]
    # Dauer
    try:
        data.append(int(row[6]))
    except ValueError:
        data.append(None)
    # Fehler
    data.append(row[7].strip())
    
    #
    # Daten in DB schreiben
    #
    sql = """
    INSERT INTO call (
        datum, 
        uhrzeit, 
        eingangsport, 
        ausgangsport, 
        eingangsnummer, 
        ausgangsnummer, 
        dauer, 
        fehler
    ) VALUES (
        %s, %s, %s, %s, %s, %s, %s, %s
    )
    """
    cur.execute(sql, data)
    
# Commit
conn.commit()

# CSV-Datei schließen
f.close()

# Anzeigen
sql = "SELECT * FROM daten"
cur.execute(sql)
print cur.fetchall()

# Datenbankverbindung trennen
cur.close()
conn.close()
lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
BoOnOdY
User
Beiträge: 112
Registriert: Samstag 22. Juli 2006, 13:38

Sehr cool :) tausend Dank

kann ich dem auch irgendwie sagen, dass wenn die Zeile schon existiert, soll er sie nicht noch mal reinkopieren?
wie msste ich das anfangen?
irgend eine if abfrage?

wie sieht so was in python aus? und kann man das einfach so machen?


ich probier ma ein bisschen rum, kann sein das ich vieleicht noch ein paar Fragen hab.

Gruß Tim :D


PS: da drauf muss jetzt noch net geantwortet werden, ich probier erst ma ein bisschen rum, und dann stell ich noch ma ne konkrete Frage, Danke schon ma :)
Zuletzt geändert von BoOnOdY am Donnerstag 3. August 2006, 16:11, insgesamt 1-mal geändert.
murph
User
Beiträge: 622
Registriert: Freitag 14. April 2006, 19:23
Kontaktdaten:

"sql" bei google einzutippen bringt wunder!
da kannst du alles herausfinden...
ich würde die art von if-abfragen der datenbank überlassen, die hat schließlich die funktionen.
http://www.cs.unm.edu/~dlchao/flake/doom/
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

BoOnOdY hat geschrieben:wenn die Zeile schon existiert, soll er sie nicht noch mal reinkopieren?
Hi Tim!

Das Einfachste für dich ist, wenn du die wichtigen Tabellenfelder mit einer Unique-Constraint zusammenfasst und so einstellst, dass diese unbedingt mit einem Wert belegt werden müssen. Dann schlägt PostgreSQL Alarm wenn du versuchst eine Zeile hinzuzufügen die es bereits gibt. Diesen Fehler kannst du dann abfangen und darauf reagieren.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

# CREATE TABLE call (
#  id serial NOT NULL,
#  datum date NOT NULL,
#  uhrzeit time NOT NULL,
#  eingangsport int4 NOT NULL,
#  ausgangsport int4 NOT NULL,
#  eingangsnummer varchar(50),
#  ausgangsnummer varchar(50),
#  dauer int4,
#  fehler varchar(50),
#  CONSTRAINT pk_call PRIMARY KEY (id),
#  CONSTRAINT c_call_unique UNIQUE (datum, uhrzeit, eingangsport, ausgangsport)
#) WITHOUT OIDS;


import psycopg2
import csv
import datetime


# Datenbank-Connection und -Cursor erstellen
conn = psycopg2.connect(user = "zopetest", password = "zopetest", database = "zopetest")
cur = conn.cursor()

# Datei öffnen
f = file("daten.csv", "r")

# CSV-Datei dem Parser übergeben
csvdata = csv.reader(f, delimiter = ';')

for row in ( row for row in csvdata if row ):
    #
    # Daten vorbereiten
    #
    data = []
    # Datum
    d, m, y = ( int(val) for val in row[0].split(".") )
    data.append(datetime.date(y, m, d))
    # Uhrzeit
    H, M, S, ms = ( int(val) for val in row[1].split(":") )
    data.append(datetime.time(H, M, S, ms))
    # Eingangsport und Ausgangsport, Eingangsnummer und Ausgangsnummer
    data += [int(row[2]), int(row[3]), row[4] or None, row[5] or None]
    # Dauer
    try:
        data.append(int(row[6]))
    except ValueError:
        data.append(None)
    # Fehler
    data.append(row[7].strip())
    
    #
    # Daten in DB schreiben
    #
    sql = """
    INSERT INTO call (
        datum, 
        uhrzeit, 
        eingangsport, 
        ausgangsport, 
        eingangsnummer, 
        ausgangsnummer, 
        dauer, 
        fehler
    ) VALUES (
        %s, %s, %s, %s, %s, %s, %s, %s
    );
    """
    try:
        cur.execute(sql, data)
    except psycopg2.IntegrityError, errdata:
        if "c_call_unique" in str(errdata):
            conn.rollback()
        else:
            raise
    # Commit nach jedem "execute", da bei Fehler ein "Rollback" durchgeführt wird.
    conn.commit()

# CSV-Datei schließen
f.close()

# Anzeigen
sql = "SELECT * FROM call"
cur.execute(sql)
print cur.fetchall()

# Datenbankverbindung trennen
cur.close()
conn.close()
lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
BoOnOdY
User
Beiträge: 112
Registriert: Samstag 22. Juli 2006, 13:38

Hey vielen Dank noch ma,

jetzt hab ich nur noch ein kleines Problem,
wie setzt sich die Uhrzeit funktion zusammen?
Man kann zwar aus meiner csv die uhrzeit mit milisekunden auslesen, ich will diese letzte Stelle aber nicht in meiner SQL tabelle haben.

danke schon ma :)

glg tim
BoOnOdY
User
Beiträge: 112
Registriert: Samstag 22. Juli 2006, 13:38

Hey,
ich hab die milisekunden entfernen wollen, und noch eine neue Spalte dazu machen wollen, und irgendwie zeigt er mir jetzt den Fehler
Traceback (most recent call last):
File "C:\Dokumente und Einstellungen\Admin\Desktop\Python_script.py", line 24, in -toplevel-
d, m, y = ( int(val) for val in row[2].split(".") )
File "C:\Dokumente und Einstellungen\Admin\Desktop\Python_script.py", line 24, in <generator expression>
d, m, y = ( int(val) for val in row[2].split(".") )
ValueError: invalid literal for int(): 23:11:35:331

Code: Alles auswählen

import psycopg2 
import csv 
import datetime 


# Datenbank-Connection und -Cursor erstellen 
conn = psycopg2.connect(user = "Import", password = "import", database = "GBC") 
cur = conn.cursor() 

# CSV-Datei öffnen 
f = file("neu.csv", "r") 

# CSV-Datei dem Parser übergeben 
csvdata = csv.reader(f, delimiter = ';') 

# Jede, nicht leere, Zeile durchlaufen 
for row in ( row for row in csvdata if row ): 
    # 
    # Daten vorbereiten (in den korrekten Datentyp umwandeln) 
    # 
    data = []
    data += row[1]
    # Datum 
    d, m, y = ( int(val) for val in row[2].split(".") ) 
    data.append(datetime.date(y, m, d)) 
    # Uhrzeit 
    H, M, S = ( int(val) for val in row[3].split(":") ) 
    data.append(datetime.time(H, M, S,)) 
    # Eingangsport und Ausgangsport, Eingangsnummer und Ausgangsnummer 
    data += [int(row[4]), int(row[5]), row[6] or None, row[7] or None] 
    # Dauer 
    try: 
        data.append(int(row[8])) 
    except ValueError: 
        data.append(None) 
    # Fehler 
    data.append(row[9].strip()) 
    
    # 
    # Daten in DB schreiben 
    # 
    sql = """ 
    INSERT INTO call (
	callrefnummer 
        datum, 
        uhrzeit, 
        eingangsport, 
        ausgangsport, 
        A-Nummer, 
        B-Nummer, 
        dauer, 
        Releasecode 
    ) VALUES ( 
        %s, %s, %s, %s, %s, %s, %s, %s 
    ) 
    """ 
   
    try: 
        cur.execute(sql, data) 
    except psycopg2.IntegrityError, errdata: 
        if "c_call_unique" in str(errdata): 
            conn.rollback() 
        else: 
            raise 
    # Commit nach jedem "execute", da bei Fehler ein "Rollback" durchgeführt wird. 
    
conn.commit() 

# CSV-Datei schließen 
f.close() 

# Anzeigen 
sql = "SELECT * FROM call" 
cur.execute(sql) 
print cur.fetchall() 

# Datenbankverbindung trennen 
cur.close() 
conn.close()    
cur.execute(sql, data) 
    
# Commit 
conn.commit() 

# CSV-Datei schließen 
f.close() 

# Anzeigen 
sql = "SELECT * FROM daten" 
cur.execute(sql) 
print cur.fetchall() 

# Datenbankverbindung trennen 
cur.close() 
conn.close()

CREATE TABLE call
(
callrefnummer varchar(50) NOT NULL,
datum date,
uhrzeit time,
eingangsport int4,
ausgangsport int4,
eingangsnummer varchar(50),
ausgangsnummer varchar(50),
dauer int4,
releasecode varchar(50),
CONSTRAINT call_pkey PRIMARY KEY (callrefnummer)
)
WITHOUT OIDS;
ALTER TABLE call OWNER TO "Tim";

und die Quelldatei sieht jetzt so aus
18471;21.07.2006;23:17:48:057;5;11;966506485768;22796581027;;CAU_NCC
18472;21.07.2006;23:18:16:107;5;11;33147700610;22796587000;;CAU_NOCAV
18473;21.07.2006;23:21:21:814;5;11;21851620113;22796589192;11762;CAU_NCC

Danke schon ma, hab jetzt schon viel rumprobiert aber net wirklich was gefunden


Gruß TIm
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

BoOnOdY hat geschrieben:ich hab die milisekunden entfernen wollen, und noch eine neue Spalte dazu machen wollen,
Hi Tim!

1. Die Nummerierung der Einträge einer Liste beginnt bei 0 und nicht bei 1.
2. Wenn du nicht weißt, warum der Fehler auftritt, dann schreibe dir ein paar Print-Anweisungen in den Quellcode, damit du nachvollziehen kannst, welchen Wert du gerade bearbeitest. Du kannst auch mit einer guten IDE arbeiten und den Code darin debuggen.

z.B.:

Code: Alles auswählen

print "row:", row
print "row[0]:", row[0]
print "row[1]:", row[1]
So etwas wirkt Wunder. Dann würdest du raus bekommen, dass du die falschen Listeneinträge heranziehst.

Wenn du einen Text wie z.B. "13:11:056" durch die Anweisung split(":")
trennst, dann musst du das Ergebnis entweder einer oder drei Variablenamen zuweisen. Das Ergebnis lässt sich nicht zwei Variablen zuweisen, da du drei Werte zurück bekommst -- nicht zwei.

Code: Alles auswählen

H, M, ms = "13:11:056".split(":") # OK
H, M = "13:11:056".split(":")       # Fehler
Lass also ``H, M, ms`` auf der linken Seite stehen und verwende ``ms`` einfach nicht weiter.

Noch Fragen?

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

gerold hat geschrieben:- text ??? --> varchar(maximale Feldgröße)

Code: Alles auswählen

#CREATE TABLE call (
[...]
#   eingangsnummer varchar(50), 
Hi!

:oops:
Richtigstellung:

Ich hatte fälschlicherweise angenommen, dass bei PostgreSQL der Datentyp TEXT gleich wie bei MsSQL nicht indizierbar ist und dessen Daten nicht in der Tabelle sondern in einem eigenen Speicherbereich gehalten werden. Deshalb hatte ich von TEXT abgeraten und stattdessen VARCHAR empfohlen.

In der Dokumentation zu PostgreSQL steht aber eindeutig, dass die Datentypen "VARCHAR", "CHAR" und "TEXT" gleich schnell sind. Auch konnte ich ein Beispiel entdecken, in dem eine Spalte vom Datentyp "TEXT" indiziert wurde. Also muss ich zugeben, dass bei PostgreSQL der Datentyp "TEXT" keine schleche Wahl für jegliche Art von Texten ist.

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
BoOnOdY
User
Beiträge: 112
Registriert: Samstag 22. Juli 2006, 13:38

Ich hätt noch ne Kleinigkeit:

Gibt es irgend ein Python Befehl, der sagt:

Wenn die Fehlerrückgabe aus der PostgreSQL war, dass der Primary Key Doppelt war, dann schreibe den Datensatz in ein "log" file und mache normal weiter.

Gruß Tim :)
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

BoOnOdY hat geschrieben:Wenn die Fehlerrückgabe aus der PostgreSQL war, dass der Primary Key Doppelt war, dann schreibe den Datensatz in ein "log" file und mache normal weiter.
Hi Tim!

Provoziere den Fehler herbei und untersuche ihn.

Wenn es einen Fehler beim INSERT gibt, dann wird dieser im "except"-Block des Programmes verarbeitet. Du musst im "except"-Block nur prüfen, ob es auch wirklich der richtige Fehler ist und nicht irgendein anderer. Schau dir also an, welchen Fehlertyp und was du als Fehlermeldung zurück bekommst und untersuche die Variable, die die Fehlermeldung enthält.

Wenn du also weißt, dass es sich um den gewünschten Fehler handelt, dann kannst du alles was du willst, zu diesem Zeitpunkt, in eine Textdatei schreiben.

Wo liegt das Problem? Beim Erkennen des Fehlers oder beim Schreiben in eine Textdatei?

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
BoOnOdY
User
Beiträge: 112
Registriert: Samstag 22. Juli 2006, 13:38

Hy,
also mir gehts darum wie ich das dann in ne text datei schreibe.

Ist dieses "rollback" einfach der ausdruck dafür, das er weiter macht und den Datensatz überspringt?

Gruß Tim :)
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

BoOnOdY hat geschrieben:also mir gehts darum wie ich das dann in ne text datei schreibe.

Ist dieses "rollback" einfach der ausdruck dafür, das er weiter macht und den Datensatz überspringt?
Hi Tim!

Wie du in eine Textdatei schreiben kannst, das erklärt dir jedes normale Python-Tutorial.

Was es mit dem Rollback auf sich hat, das erkläre ich jetzt mal.

Man kann Datenbankanweisungen einzeln ausführen oder mehrere Anweisungen zusammenfassen.

Mit ``BEGIN TRANS``, ``BEGIN TRANSACTION`` oder wie bei PostgreSQL mit ``BEGIN`` wird so ein Anweisungsblock eingeleitet.

Mit ``COMMIT TRANS``, ``COMMIT TRANSACTION`` oder wie bei PostgreSQL mit ``COMMIT`` wird so ein Anweisungsblock abgeschlossen.

So lange die Anweisung ``COMMIT`` nicht ausgeführt wurde, werden die Änderungen nicht öffentlich. Beendet der Benutzer die Verbindung zur Datenbank ohne vorher den Block mit ``COMMIT`` abzuschließen, wird alles was innerhalb dieses Blockes geändert wurde einfach verworfen.

Die Änderungen die innerhalb eines solchen Blocks passieren, werden erst dann für andere, angemeldete Benutzer sichtbar, wenn der Block mit ``COMMIT`` abgeschlossen wurde.

Tritt ein Fehler während der Abarbeitung der SQL-Anweisungen eines solchen Blockes auf, dann werden alle vorhergehenden Änderungen, die innerhalb dieses Blocks passiert sind, rückgängig gemacht.

So ein Anweisungsblock könnte so aussehen:

Code: Alles auswählen

BEGIN;
INSERT INTO adressen (vorname, nachname) VALUES ('Gerold', 'Penz');
INSERT INTO adressen (vorname, nachname) VALUES ('Ludwig', 'Bucher');
COMMIT;
Tritt z.B. beim Hinzufügen des Datensatzes "Ludwig Bucher" ein Fehler auf, dann wird auch der Datensatz "Gerold Penz" nicht hinzugefügt.

psycopg2 sendet die Anweisung ``BEGIN`` bereits beim Initialisieren der Connection oder des Cursors (genau weiß ich es jetzt nicht mehr -- ist aber auch egal). Wenn du also eine ändernde SQL-Anweisung wie z.B. ``UPDATE`` oder ``INSERT`` ausführst, dann musst du danach nur noch das ``COMMIT`` senden. Das funktioniert mit der Anweisung ``conn.commit()``. Dieses ``conn.commit()`` schicht jetzt nicht nur ein ``COMMIT`` an die Datenbank, sondern auch ein neues ``BEGIN``. Damit ist wieder eine neue Transaktion eröffnet. Möchtest du jetzt wieder etwas ändern, dann genügt wieder ein einfaches ``conn.commit()``.

Möchtest du gezielt alle Änderungen eines Blockes verwerfen, dann kannst du ein ``ROLLBACK`` an die Datenbank schicken. Das funktioniert aber nur innerhalb eines Blocks. Wenn der Block einmal mit ``COMMIT`` abgeschlossen wurde, ist es zu spät. Dieses ROLLBACK kannst du ziemlich einfach mit der Anweisung ``conn.rollback()`` an die Datenbank schicken. Aber, genauso wie auch ein ``conn.commit()`` eine neue Transaktion eröffnet, eröffnet auch ein ``conn.rollback()`` eine neue Transaktion. Der Unterschied ist nur, dass vorher die Änderungen des Blocks, verworfen werden.

Das ist auch der Grund, weshalb ich im vorherigen Beispiel nach jeder Änderung ein ``conn.commit()`` ausführe. Tritt nämlich ein Fehler auf, möchte ich nicht alle Änderungen rückgängig machen, sondern nur das Hinzufügen des aktuellen Datensatzes abbrechen und dann mit dem nächsten Datensatz weiter machen.

Allerdings ist in so einem Fall bereits ein Fehler in der Datenbank aufgetreten. Die Datenbank beendet somit den Block, also die Transaktion, und erwartet ein neues ``BEGIN`` von der Client-Anwendung. Mit ``conn.commit()`` kann man eine fehlerhafte Transaktion nicht abschließen. Da zeigt uns PostgreSQL den Vogel und teilt uns mit, dass es ja einen Fehler gegeben hat, den PostgreSQL nicht abschließen kann. Deshalb beginne ich die neue Transaktion mit ``conn.rollback()``, was der Datenbank mitteilt, dass die letzte Änderung verworfen werden kann. Da ``conn.rollback()`` zusätzlich auch eine neue Transaktion eröffnet, steht einem erneuten Schleifendurchlauf nichts mehr im Weg.

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Antworten