Kleines Datenbankbeispiel (allgemein und SQLite)

Code-Stücke können hier veröffentlicht werden.
Antworten
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Hallo!

Dieses kleine Datenbankbeispiel greift auf SQLite zu. Dank der DB-API ist dieses Beispiel aber ohne großen Aufwand auf viele andere Datenbanken adaptierbar.

Das Beispiel zeigt, sehr einfach gehalten, das Erstellen einer Tabelle mit Indizes (CREATE TABLE und CREATE INDEX), das Befüllen einer Tabelle (INSERT INTO) und das Abfragen von Daten aus einer Tabelle (SELECT).

Beim Abfragen der Daten werden diese in ein Cursor-Objekt gelegt. Dieses Cursor-Objekt kann wie ein Generator nur einmal von oben nach unten durchlaufen werden. Bei jeder Iteration enthält das Cursor-Objekt eine Datenreihe.

Das läuft in etwa so ab. Zuerst wird die SELECT-Anweisung ausgeführt. Danach kann über das Cursor-Objekt iteriert werden:

Code: Alles auswählen

cur.execute(sql)
for datenfeld1, datenfeld2, datenfeld3 in cur:
    print datenfeld1, datenfeld2, datenfeld3
Und hier das ganze Beispiel:

Code: Alles auswählen

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

import os
import sys
import sqlite3
from pprint import pprint

FILENAME = "addresses.sdb3"


def create_db_structure(conn):
    """
    Erstellt die Tabellenstruktur. (In diesem Fall nur die Tabelle "addresses"
    und die Indizes für den schnelleren Datenzugriff
    """
    
    # SQL-Anweisung zum Erstellen der Tabelle "addresses". Wird ein Feld mit
    # INTEGER PRIMARY KEY deklariert, dann wird dieses Feld automatisch zum
    # Autowert-Feld. Befüllt man später die Tabelle, dann muss man nicht mehr
    # explizit einen Wert in das Autowert-Feld schreiben. Die Datenbank zählt
    # das Feld automatisch hoch. Diese Syntax gilt aber nur für SQLite. Andere
    # Datenbanken haben eine leicht geänderte Syntax für so etwas.
    sql = """
    CREATE TABLE addresses (
      id INTEGER PRIMARY KEY,
      first_name TEXT,
      last_name TEXT,
      address TEXT
    )
    """
    # Diese SQL-Anweisung liefert keine Daten zurück und kann deshalb direkt
    # über das Connection-Objekt ausgeführt werden.
    conn.execute(sql)
    # Nur ein ``conn.commit()`` stellt sicher, dass die Daten in die Datenbank
    # geschrieben werden.
    conn.commit()
    
    sql = """
    CREATE INDEX ix_addresses_first_name ON addresses (first_name)
    """
    conn.execute(sql)
    conn.commit()
    
    sql = """
    CREATE INDEX ix_addresses_last_name ON addresses (last_name)
    """
    conn.execute(sql)
    conn.commit()
    

def insert_address(conn, first_name, last_name, address):
    # Hier sieht man deutlich, dass ich mich nicht selber um die Ersetzung
    # der Parameter kümmere. Ich setze Fragezeichen (?) als Platzhalter ein
    # und überlasse die korrekte Ersetzung der Datenbankschnittstelle.
    # Welcher Platzhalter dafür eingesetzt werden kann, erfährt man mit
    # dem Befehl ``sqlite3.paramstyle``.
    # Man achte auch darauf, dass hier die Platzhalter nicht in Anführungszeichen
    # gesetzt wurden. Auch darum kümmert sich die Datenbankschnittstelle selbst.
    sql = """
    INSERT INTO addresses (
      first_name, last_name, address
    ) VALUES (
      ?, ?, ?
    )
    """
    conn.execute(sql, (first_name, last_name, address))
    conn.commit()


def select_addresses(conn):
    sql = """
    SELECT id, first_name, last_name, address
    FROM addresses
    ORDER by id, first_name, last_name
    """
    # Wenn man von der Datenbank Daten zurück bekommt, dann muss man statt dem
    # Connection-Objekt das Cursor-Objekt verwenden. Dieses erhält man direkt
    # vom Connection-Objekt und sollte vor jeder Abfrage neu geholt werden.
    cur = conn.cursor()
    # Die Daten werden von ``execute()`` geholt und sind danach direkt über das
    # Cursor-Objekt abrufbar.
    cur.execute(sql)
    
    return cur


def main():
    
    # Zum TESTEN -- Alte DB löschen
    if os.path.isfile(FILENAME):
        os.remove(FILENAME)
    
    # SQLite-Connection
    conn = sqlite3.connect(FILENAME)
    
    # Tabelle erstellen und mit Werten füllen
    create_db_structure(conn)
    addresses = (
        ("Martin", "Huber", "Wallerweg 1"),
        ("Maria", "Sauter", "Schupfweg 2"),
    )
    for first_name, last_name, address in addresses:
        insert_address(conn, first_name, last_name, address)
    
    # Adressen aus der Datenbank holen und anzeigen
    adr_cursor = select_addresses(conn)
    print list(adr_cursor)
    
    # Jede einzelne Adresse durchlaufen und formatiert anzeigen.
    #
    # WICHTIG!!! select_addresses gibt ein Cursor-Objekt zurück. Dieses
    # verhält sich wie ein Generator und kann nur einmal durchlaufen werden.
    # Deshalb müssen wir hier ``select_addresses`` noch einmal aufrufen um
    # die Adressen durchlaufen zu können.
    #
    # Braucht man die Adressen mehrmals hintereinander, dann ist es oft sinnvoll,
    # die Adressen in einer Liste oder einer anderen Speicherstruktur im Programm 
    # zwischenzuspeichern. Das braucht natürlich mehr Arbeitsspeicher. Man muss
    # selber entscheiden wie man auf die Daten zugreifen möchte und was sinnvoll ist.
    adr_cursor = select_addresses(conn)
    for id, first_name, last_name, address in adr_cursor:
        print "%i - %s - %s - %s" % (id, first_name, last_name, address)
    
    # Man kann die Daten vor dem Gebrauch in eine Liste legen.
    adr_cursor = select_addresses(conn)
    adr_list = list(adr_cursor)
    # Das hat den **grossen** Nachteil, dass diese Liste jetzt im Speicher liegt. 
    # Und das mit allen Adressen die mit der SELECT-Anweisung aus der Datenbank geholt 
    # wurden. Wenn der Speicherverbrauch überschaubar bleibt, dann ist das aber kein
    # Problem und bietet den Vorteil, dass man mehrmals (und nicht nur einmal,
    # wie oben) auf die Adressen zugreifen kann.
    
    # Man kann durch eine Liste iterieren...
    for id, first_name, last_name, address in adr_list:
        print "%i - %s - %s - %s" % (id, first_name, last_name, address)
    
    # ...oder gezielt auf eine Datenzeile zugreifen.
    # Jede Datenzeile ist in der Liste einzeln erreichbar.
    print adr_list[1]
    print adr_list[0]
    
    # Vorher kann man zur Übersichtlichkeit die Daten auch an Variablen binden.
    id = adr_list[0][0]
    first_name = adr_list[0][1]
    last_name = adr_list[0][2]
    address = adr_list[0][3]
    print id, first_name, last_name, address
    
    # Die Verbindung zur Datenbank sollte geschlossen werden, wenn man sie
    # nicht mehr braucht.
    conn.close()


if __name__ == "__main__":
    main()

Code: Alles auswählen

[(1, u'Martin', u'Huber', u'Wallerweg 1'), (2, u'Maria', u'Sauter', u'Schupfweg 2')]
1 - Martin - Huber - Wallerweg 1
2 - Maria - Sauter - Schupfweg 2
1 - Martin - Huber - Wallerweg 1
2 - Maria - Sauter - Schupfweg 2
(2, u'Maria', u'Sauter', u'Schupfweg 2')
(1, u'Martin', u'Huber', u'Wallerweg 1')
1 Martin Huber Wallerweg 1
Vielleicht kann ja irgendjemandem etwas mit diesem Beispiel anfangen.

mfg
Gerold
:-)

PS: http://docs.python.org/lib/module-sqlite3.html
Zuletzt geändert von gerold am Donnerstag 9. April 2009, 06:04, insgesamt 5-mal geändert.
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:

Hallo!

Ich habe das Beispiel jetzt mit mehr Kommentaren versehen. Bitte lies die Kommentare genau durch wenn du dich noch nicht mit Datenbankzugriffen auskennst. Darin habe ich einige Dinge genauer erklärt.

Obiges Beispiel könnte man eigentlich als Fortsetzung zu dieser Anleitung sehen: http://www.python-forum.de/topic-6157.html

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
ete
User
Beiträge: 218
Registriert: Montag 19. Februar 2007, 13:19
Kontaktdaten:

Hallo Gerold!

Danke für dieses Beispiel, es ist sehr hilfreich!

Leider weiss ich immernoch nicht wann ich execute, commit und cursor verwenden muss.

Könntest du nochmal einen Erklärung dazu geben?

Danke und liebe Grüsse
:wink:
Stefanie
Antworten