Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.

...powered by haas.homelinux.net...

Inhaltsverzeichnis
1 Einleitung
2 Überblick über Python
3 Die Arbeit mit Python
4 Der interaktive Modus
5 Grundlegendes zu Python-Programmen
6 Kontrollstrukturen
7 Das Laufzeitmodell
8 Basisdatentypen
9 Benutzerinteraktion und Dateizugriff
10 Funktionen
11 Modularisierung
12 Objektorientierung
13 Weitere Spracheigenschaften
14 Mathematik
15 Strings
16 Datum und Zeit
17 Schnittstelle zum Betriebssystem
18 Parallele Programmierung
19 Datenspeicherung
20 Netzwerkkommunikation
21 Debugging
22 Distribution von Python-Projekten
23 Optimierung
24 Grafische Benutzeroberflächen
25 Python als serverseitige Programmiersprache im WWW mit Django
26 Anbindung an andere Programmiersprachen
27 Insiderwissen
28 Zukunft von Python
A Anhang
Stichwort

Download:
- ZIP, ca. 4,8 MB
Buch bestellen
Ihre Meinung?

Spacer
 <<   zurück
Python von Peter Kaiser, Johannes Ernesti
Das umfassende Handbuch - Aktuell zu Python 2.5
Buch: Python

Python
gebunden, mit CD
819 S., 39,90 Euro
Galileo Computing
ISBN 978-3-8362-1110-9
Pfeil 19 Datenspeicherung
  Pfeil 19.1 Komprimierte Dateien lesen und schreiben – gzStandardbibliothekgzipip
  Pfeil 19.2 XML
    Pfeil 19.2.1 DOM – Document Object Model
    Pfeil 19.2.2 SAX – Simple API for XML
    Pfeil 19.2.3 ElementTree
  Pfeil 19.3 Datenbanken
    Pfeil 19.3.1 Pythons eingebaute Datenbank – sqlite3
    Pfeil 19.3.2 MySQLdb
  Pfeil 19.4 Serialisierung von Instanzen – pickle
  Pfeil 19.5 Das Tabellenformat CSV – csv
  Pfeil 19.6 Temporäre Dateien – tempfile


Galileo Computing - Zum Seitenanfang

19.3 Datenbanken  Zur nächsten ÜberschriftZur vorigen Überschrift

Je mehr Daten ein Programm verwalten muss und je komplexer die Struktur dieser Daten wird, desto größer wird der programmtechnische Aufwand für die dauerhafte Speicherung und Verwaltung der Daten. Außerdem gibt es eine ganze Reihe von Aufgaben wie das Lesen, Schreiben oder Aktualisieren, die in fast jedem Programm gebraucht werden, aber immer wieder neu implementiert werden müssten.

Abhilfe für diese Problematik wurde dadurch geschaffen, dass man eine Abstraktionsschicht zwischen dem benutzenden Programm und dem physikalischen Massenspeicher einzog, die sogenannte Datenbank. Dabei erfolgt die Kommunikation zwischen Benutzerprogramm und Datenbank über eine vereinheitlichte Schnittstelle.

Abbildung 19.3  Die Datenbankschnittstelle

Das Datenbanksystem nimmt dabei Abfragen, sogenannte Queries, entgegen und gibt alle Datensätze zurück, die den Bedingungen der Abfragen genügen.

Wir werden uns in diesem Kapitel ausschließlich mit sogenannten relationalen Datenbanken beschäftigen, die einen Datenbestand in Tabellen organisieren. [Der Attribut »relational« geht auf den Begriff der Relation aus der Mathematik zurück. Vereinfacht gesagt ist eine Relation eine Zuordnung von Elementen zweier oder mehrerer Mengen. ] Für die Abfragen in relationalen Datenbanken wurde eine eigene Sprache entwickelt, deren Name SQL (Structured Query Language, dt. strukturierte Abfragesprache) ist. SQL ist zu komplex, um es in diesem Kapitel erschöpfend zu beschreiben. Wir werden hier nur auf grundlegende Befehle von SQL eingehen, die nötig sind, um das Prinzip von Datenbanken und deren Anwendung in Python zu verdeutlichen.

SQL ist standardisiert und wird von eigentlich allen Datenbanksystemen unterstützt. Dabei ist zu beachten, dass die Systeme immer nur Teilmengen der Sprache implementieren und teilweise geringfügig abändern. Aus diesem Grund werden wir Sie hier in das SQL einführen, das von SQLite, der Standarddatenbank in Python, genutzt wird. Im Abschnitt über die Nutzung des Datenbankservers MySQL gehen wir dann auf die kleinen Unterschiede ein.

Neben der Abfragesprache SQL ist in Python auch die Schnittstelle der Datenbankmodule standardisiert. Dies hat für den Programmierer den angenehmen Nebeneffekt, dass sein Code mit minimalen Anpassungen auf allen Datenbanksystemen lauffähig ist, die diesen Standard implementieren. Die genaue Definition dieser sogenannten Python Database API Specification können Sie im Internet unter der Adresse http://www.python.org/dev/peps/pep-0249/ nachlesen.

Bevor wir uns aber eingehend mit der Abfragesprache SQL selbst beschäftigen, werden wir eine kleine Beispieldatenbank erarbeiten und uns überlegen, welche Operationen man überhaupt ausführen könnte. Anschließend werden wir dieses Beispiel mithilfe von SQLite implementieren und dabei auf Teile der Abfragesprache SQL und die Verwendung in Python-Programmen eingehen.

Stellen wir uns vor, wir müssten das Lager eines Computerversands verwalten. Wir wären dafür verantwortlich, dass die gelieferten Teile an der richtigen Stelle im Lager aufbewahrt werden, wobei für jede Komponente der Lieferant, der Lieferzeitpunkt und die Nummer des Fachs im Lager gespeichert werden soll. Für Kunden, die bei dem Versand ihre Rechner bestellen, werden die entsprechenden Teile reserviert, und diese sind dann für andere Kunden nicht mehr verfügbar. Außerdem sollen wir Listen mit allen Kunden und Lieferanten der Firma bereitstellen.

Um ein Datenbankmodell für dieses Szenario zu erstellen, legen wir zuerst eine Tabelle namens »Lager« an, die alle im Lager befindlichen Komponenten enthält. Wir gehen der Einfachheit halber davon aus, dass unser Lager in 100 Fächer eingeteilt ist, die fortlaufend nummeriert sind. Dabei kann jedes Fach nur ein einzelnes Computerteil aufnehmen. Eine entsprechende Tabelle mit ein paar Beispieldatensätzen für das Lager könnte dann wie folgt aussehen, wenn wir zusätzlich den Lieferanten und den Reservierungsstatus speichern wollen:


Tabelle 19.3  Tabelle »Lager« für den Lagerbestand
Fachnummer Seriennummer Komponente Lieferant Reserviert

1

26071987

Grafikkarte Typ 1

FC

0

2

19870109

Prozessor Typ 13

LPE

57

10

06198823

Netzteil Typ 3

FC

0

25

11198703

LED-Lüfter

FC

57

26

19880105

Festplatte 128 GB

LPE

12


Die Spalte »Lieferant« enthält dabei das Kürzel der liefernden Firma, und das Feld »Reserviert« ist auf »0« gesetzt, wenn der betreffende Artikel noch nicht von einem Kunden reserviert wurde. Ansonsten enthält das Feld die Kundenummer des reservierenden Kunden. In der Tabelle werden nur die belegten Fächer gespeichert, weshalb alle Fächer, für die kein Eintrag existiert, mit neuen Teilen gefüllt werden können.

Die ausführlichen Informationen zu Lieferanten und Kunden werden in zwei weiteren Tabellen namens »Lieferanten« und »Kunden« abgelegt:


Tabelle 19.4  Tabelle »Lieferanten«
Kurzname Name Telefonnummer

FC

FiboComputing Inc.

011235813

LPE

LettgenPetersErnesti

026741337

GC

Golden Computers

016180339



Tabelle 19.5  Tabelle »Kunden«
Kundennummer Name Anschrift

12

Heinz Elhurg

Turnhallenstr. 1, 3763 Sporthausen

57

Markus Altbert

Kämperweg 24, 2463 Duisschloss

64

Steve Apple

Podmacstr 2, 7467 Iwarhausen


Damit wir als Lagerverwalter von dieser Datenbank profitieren können, müssen wir die Möglichkeit haben, den Datenbestand zu manipulieren. Wir brauchen Routinen, um neue Kunden und Lieferanten hinzuzufügen, ihre Daten beispielsweise bei einem Umzug zu aktualisieren oder sie auf Wunsch aus unserer Datenbank zu entfernen. Auch in die Tabelle »Lager« müssen neue Einträge eingefügt und alte gelöscht oder angepasst werden können. Um die Datenbank aktuell zu halten, benötigen wir also Funktionen zum Hinzufügen und Löschen.

Wirklich nützlich wird die Datenbank aber erst, wenn wir die enthaltenen Daten nach bestimmten Kriterien abfragen können. Im einfachsten Fall könnten wir beispielsweise einfach nur eine Liste aller Kunden oder Lieferanten anfordern oder uns informieren wollen, welche Fächer zurzeit belegt sind. Uns könnte aber auch interessieren, ob und wenn ja, welche Artikel der Kunde mit dem Namen »Markus Altbert« reserviert hat und wo diese gelagert werden oder welche Komponenten wir von dem Lieferanten mit der Telefonnummer »011235813« nachbestellen müssen, weil sie nicht mehr vorhanden oder bereits reserviert sind. Bei diesen Operationen werden immer Datensätze nach bestimmten Kriterien ausgewählt und an das aufrufende Benutzerprogramm zurückgegeben.

Nach dieser theoretischen Vorbereitung werden wir uns der Implementation des Beispiels in einer SQLite-Datenbank zuwenden.


Galileo Computing - Zum Seitenanfang

19.3.1 Pythons eingebaute Datenbank – sqlite3  Zur nächsten ÜberschriftZur vorigen Überschrift

SQLite ist ein sehr einfaches Datenbanksystem, das seine Daten in normalen Dateien abspeichert. Trotzdem ist es extrem schnell und auch für verhältnismäßig große Datenmengen geeignet.

In Python muss man das Modul sqlite3 importieren, um mit der Datenbank zu arbeiten. Anschließend muss man eine Verbindung zu der Datenbank aufbauen, indem man die connect-Funktion, die ein Connection-Objekt zu der Datenbank zurückgibt, aufruft und ihr den Dateinamen für die Datenbank übergibt:

import sqlite3 
connection = sqlite3.connect("lagerverwaltung.db")

Die Dateiendung kann frei gewählt werden und hat keinerlei Einfluss auf die Funktionsweise der Datenbank. Obiger Code führt dazu, dass die Datenbank, die in der Datei lagerverwaltung.db im selben Verzeichnis wie das Programm liegt, eingelesen und mit dem Connection-Objekt connection verbunden wird. Wenn es noch keine Datei mit dem Namen lagerverwaltung.db gibt, so wird eine leere Datenbank erzeugt und die Datei angelegt.

Oft benötigt man eine Datenbank nur während des Programmlaufs, um Daten zu verwalten oder zu ordnen, ohne dass diese dauerhaft auf der Festplatte gespeichert werden müssen. Zu diesem Zweck gibt es die Möglichkeit, eine Datenbank im Arbeitsspeicher zu erzeugen, indem man anstatt eines Dateinamens den String ":memory:" an die connect-Methode übergibt:

>>> connection = sqlite3.connect(":memory:")

Um mit der verbundenen Datenbank arbeiten zu können, werden sogenannte Cursor (dt. Positionsanzeigen) benötigt. Einen Cursor kann man sich ähnlich wie den blinkenden Strich in Textverarbeitungsprogrammen als aktuelle Bearbeitungsposition innerhalb der Datenbank vorstellen. Erst mit solchen Cursorn können wir Datensätze verändern oder abfragen, wobei es zu einer Datenbankverbindung beliebig viele Cursor geben kann. Ein neuer Cursor kann mithilfe der cursor-Methode des Connection-Objekts erzeugt werden:

cursor = connection.cursor()

Neue Tabellen anlegen

Nun können wir endlich unser erstes SQL-Statement an die Datenbank schicken, um unsere Tabellen anzulegen. Für das Anlegen unserer Tabelle »Lager« sähe das SQL-Statement folgendermaßen aus:

CREATE TABLE lager ( 
    fachnummer INTEGER, seriennummer INTEGER, komponente TEXT, 
    lieferant TEXT, reserviert INTEGER 
)

Alle großgeschriebenen Wörter sind Bestandteile der Sprache SQL. Es ist allerdings so, dass SQL nicht zwischen Groß- und Kleinschreibung unterscheidet und wir deshalb auch alles hätten kleinschreiben können. Wegen der besseren Lesbarkeit werden wir SQL-Schlüsselwörter immer komplett groß- und von uns vergebene Namen durchgängig kleinschreiben.

Die Zeichenketten INTEGER und TEXT hinter den Spaltennamen geben den Datentyp an, der in den Spalten gespeichert werden soll. Sinnvollerweise werden die Spalten fachnummer, seriennummer und reserviert als Ganzzahlen und die Spalten komponente und lieferant als Zeichenketten definiert. SQLite kennt mehrere solcher Datentypen, in die Python-Datentypen beim Schreiben der Datenbank automatisch umgewandelt werden, wie es die folgende Tabelle zeigt:


Tabelle 19.6  So konvertiert SQLite beim Schreiben der Daten.
Python-Datentyp SQLite-Datentyp

None

NULL

int

INTEGER

long

INTEGER

float

REAL

str (UTF8-Kodiert)

TEXT

unicode

TEXT

Buffer

BLOB


Es ist auch möglich, andere Datentypen in SQLite-Datenbanken abzulegen, wenn entsprechende Konvertierungsfunktionen definiert wurden. Wie das genau erreicht werden kann, wird später behandelt.

Nun können wir das SQL-Statement mithilfe der execute-Methode des Cursor-Objekts an die SQLite-Datenbank senden:

cursor.execute("""CREATE TABLE lager ( 
    fachnummer INTEGER, seriennummer INTEGER, 
    komponente TEXT, lieferant TEXT, reserviert INTEGER)""")

Die Tabellen für die Lieferanten und Kunden erzeugen wir auf die gleiche Weise:

cursor.execute("""CREATE TABLE lieferanten ( 
    kurzname TEXT, name TEXT, telefonnummer TEXT)""") 
 
cursor.execute("""CREATE TABLE kunden ( 
    kundennummer INTEGER, name TEXT, anschrift TEXT)""")

Daten in die Tabellen einfügen

Als Nächstes werden wir die noch leeren Tabellen mit unseren Beispieldaten füllen. Zum Einfügen neuer Datensätze in eine bestehende Tabelle dient das INSERT-Statement, das für den ersten Beispieldatensatz folgendermaßen aussieht:

INSERT INTO lager VALUES ( 
    1, 26071987, 'Grafikkarte Typ 1', 'FC', 0 
)

Innerhalb der Klammern hinter VALUES stehen die Werte für jede einzelne Spalte in der gleichen Reihenfolge, wie auch die Spalten selbst definiert wurden. Wie bei allen anderen Datenbankabfragen auch können wir per execute unser Statement abschicken:

cursor.execute("""INSERT INTO lager VALUES ( 
    1, 26071987, 'Grafikkarte Typ 1', 'FC', 0)""")

Beim Einfügen von Datensätzen müssen Sie allerdings beachten, dass die neuen Daten nicht sofort nach Ausführen eines INSERT-Statements in die Datenbank daten geschrieben werden, sondern vorerst nur im Arbeitsspeicher liegen. Um sicherzugehen, dass die Daten wirklich auf der Festplatte landen und damit dauerhaft gespeichert sind, muss man die commit-Methode des Connection-Objekts aufrufen: [Dies ist deshalb notwendig, damit die Datenbank transaktionssicher ist. Transaktionen sind Ketten von Operationen, die vollständig ausgeführt werden müssen, damit die Konsistenz der Datenbank erhalten bleibt. Stellen Sie sich einmal vor, bei einer Bank würde während einer Überweisung zwar das Geld von Ihrem Konto abgebucht, jedoch aufgrund eines Fehlers nicht dem Empfänger gutgeschrieben werden. Mit der Methode rollback können alle Operationen seit dem letzten commit-Aufruf wieder rückgängig gemacht werden, um solche Probleme zu vermeiden. ]

connection.commit()

In der Regel werden die Daten, die wir in die Datenbank einfügen wollen, nicht schon vor dem Programmlauf bekannt sein und deshalb auch nicht in Form von String-Konstanten im Quellcode stehen. Stattdessen werden es Benutzereingaben oder Berechnungsergebnisse sein, die wir dann als Python-Instanzen im Speicher haben. Auf den ersten Blick scheint für solche Fälle der Formatierungsoperator % für Strings ein geeignetes Mittel zu sein, und die letzte INSERT-Anweisung hätte auch folgendermaßen zusammengebaut werden können:

>>> werte = (1, 26071987, "Grafikkarte Typ 1", "FC", 0) 
>>> "INSERT INTO lager VALUES (%d, %d, '%s', '%s', %d)" % werte 
'INSERT INTO lager VALUES (1, 26071987, 'Grafikkarte Typ 1', 'FC', 0)'

Diese auf den ersten Blick sehr elegante Methode entpuppt sich bei genauer Betrachtung aber als gefährliche Sicherheitslücke. Betrachten wir einmal folgende INSERT-Anweisung, die einen neuen Lieferanten in die Tabelle »Lieferanten« einfügen soll:

>>> werte = ("DR", "Danger Electronics", 
             "666'); Hier kann Schadcode stehen") 
>>> "INSERT INTO lieferanten VALUES ('%s', '%s', '%s')" % werte 
'INSERT INTO lager VALUES ('DR', 'Danger Electronics', '666'); Hier kann Schadcode stehen')'

Wie Sie sehen, haben wir dadurch, dass der Wert für die Telefonnummer den String "');" enthält, die SQL-Abfrage verunstaltet, sodass der Versuch, sie auszuführen, zu einem Fehler führen und damit unser Programm zum Absturz bringen würde. Durch den außerdem enthaltenen Text "Hier kann Schadcode stehen" wird angedeutet, dass es unter Umständen sogar möglich ist, eine Abfrage so zu manipulieren, dass wieder gültiger SQL-Code dabei herauskommt, wobei jedoch eine andere Operation als beabsichtigt (zum Beispiel das Auslesen von Benutzerdaten) ausgeführt wird. [Man nennt diese Form des Angriffs auf verwundbare Programme auch SQL Injection. ]

Verwenden Sie deshalb niemals die String-Formatierung zur Übergabe von Parametern in SQL-Abfragen!

Um sichere Parameterübergaben durchzuführen, schreibt man in den Query-String an die Stelle, an der der Parameter stehen soll, ein Fragezeichen und übergibt der execute-Methode ein Tupel mit den entsprechenden Werten als zweiten Parameter:

werte = ("DR", "Danger Electronics", 
         "666'); Hier kann Schadcode stehen") 
sql = "INSERT INTO lieferanten VALUES (?, ?, ?)" 
cursor.execute(sql, werte)

In diesem Fall kümmert sich SQLite darum, dass die übergebenen Werte korrekt umgewandelt werden und es zu keinen Sicherheitslücken durch böswillige Parameter kommen kann.

Analog zur String-Formatierung gibt es auch hier die Möglichkeit, den übergebenen Parametern Namen zu geben und statt der tuple-Instanz mit einem Dictionary zu arbeiten. Dazu wird im Query-String statt des Fragezeichens ein Doppelpunkt, gefolgt von dem symbolischen Namen des Parameters, geschrieben und das passende Dictionary als zweiter Parameter an execute übergeben:

werte = {"kurz" : "DR", "name" : "Danger Electronics", 
         "telefon" : "123456"} 
sql = "INSERT INTO lieferanten VALUES (:kurz, :name, :telefon)" 
cursor.execute(sql, werte)

Mit diesem Wissen können wir unsere Tabellen elegant und sicher mit Daten füllen:

for row in ((1, "2607871987", "Grafikkarte Typ 1", "FC", 0), 
            (2, "19870109", "Prozessor Typ 13", "LPE", 57), 
            (10, "06198823", "Netzteil Typ 3", "FC", 0), 
            (25, "11198703", u"LED-Lüfter", "FC", 57), 
            (26, "19880105", "Festplatte 128 GB", "LPE", 12)): 
    cursor.execute("INSERT INTO lager VALUES (?,?,?,?,?)", row)

Ihnen ist bestimmt aufgefallen, dass der String "LED-Lüfter" als einziger durch das führende »u« in eine unicode-Instanz überführt worden ist. Das ist deshalb notwendig, weil SQLite nur mit UTF-8 kodierte Strings richtig verarbeiten kann. Wie Sie aber seit Abschnitt 8.5.3, »Strings – str, unicode«, über String-Kodierung wissen, werden meist standardmäßig andere Kodierungsverfahren verwendet, was zu Fehlern beim Datenbankzugriff führen würde. Durch die Umwandlung in eine unicode-Instanz sind wir in jedem Fall auf der sicheren Seite. Zusätzlich könnten Sie den Umlaut ü auch durch die Escape-Sequenz \xfc kodieren, was zusätzlich den Vorteil hätte, dass Ihr Quellcode nur aus ASCII-Zeichen bestünde und somit unabhängig von der Lokalisierung des Betriebssystems überall lauffähig ist.

Generell ist es problemlos möglich, an alle im Modul sqlite3 enthaltenen Funktionen Unicode-Strings zu übergeben.

Strukturen wie die obige for-Schleife, die die gleiche Datenbankoperation sehr oft für jeweils andere Daten durchführen, kommen sehr häufig vor und bieten großes Optimierungspotenzial. Aus diesem Grund haben cursor-Instanzen zusätzlich noch die Methode executemany, die als zweiten Parameter eine Sequenz oder ein anderes iterierbares Objekt erwartet, das die Daten für die einzelnen Operationen enthält. Wir nutzen executemany, um unsere Tabellen »Lieferanten« und »Kunden« mit Daten zu füllen:

lieferanten = (("FC", "FiboComputing Inc.", "011235813"), 
               ("LPE", "LettgenPetersErnesti", "026741337"), 
               ("GC", "Golden Computers", "016180339")) 
cursor.executemany("INSERT INTO lieferanten VALUES (?,?,?)", 
                   lieferanten) 
 
kunden = ((12, "Heinz Elhurg", 
           "Turnhallenstr. 1, 3763 Sporthausen"), 
          (57, "Markus Altbert", 
           u"K\xe4mperweg 24, 2463 Duisschloss"), 
          (64, "Steve Apple", 
           "Podmacstr 2, 7467 Iwarhausen")) 
cursor.executemany("INSERT INTO kunden VALUES (?,?,?)", kunden)

Nun haben wir gelernt, wie man Datenbanken und Tabellen anlegt und diese mit Daten füllt. Im nächsten Schritt wollen wir uns mit dem Abfragen von Daten beschäftigen.

Daten abfragen

Um Daten aus der Datenbank abzufragen, dient das SELECT-Statement. SELECT erwartet als Parameter durch Kommata getrennt die Spalten, die uns von den Datensätzen interessieren, und den Tabellennamen der Tabelle, aus der wir abfragen wollen. Standardmäßig werden alle Zeilen aus der abgefragten Tabelle zurückgegeben. Mit einer WHERE-Klausel können wir nur bestimmte Datensätze auswählen, indem wir Bedingungen für die Auswahl angeben. Stark vereinfacht ist ein SELECT-Statement folgendermaßen aufgebaut:

SELECT <spaltenliste> FROM <tabellenname> [WHERE <bedingung>]

Wie durch die eckigen Klammern angedeutet wird, ist die WHERE-Klausel optional und kann entfallen.

Wenn wir beispielsweise alle belegten Fachnummern und die dazugehörigen Komponenten abfragen wollen, können wir das mit dem folgenden Statement tun:

SELECT fachnummer, komponente FROM lager

Auch bei Datenabfragen benutzen wir die execute-Methode des Cursor-Objekts, um der Datenbank unser Anliegen mitzuteilen. Anschließend können wir uns mittels cursor.fetchall alle Datensätze zurückgeben lassen, die unsere Abfrage ergeben hat:

>>> cursor.execute("SELECT fachnummer, komponente FROM lager") 
>>> cursor.fetchall() 
[(1, u'Grafikkarte Typ 1'), (2, u'Prozessor Typ 13'), 
 (10, u'Netzteil Typ 3'), (25, u'LED-L\xfcfter'), 
 (26, u'Festplatte 128 GB')]

Der Rückgabewert von fetchall ist eine Liste, die für jeden Datensatz ein Tupel mit den Werten der angeforderten Spalten enthält. [Standardmäßig werden bei der Abfrage alle TEXT-Spalten als unicode-Instanzen zurückgegeben. Wie Sie dieses Verhalten anpassen können, werden wir später behandeln. ]

Mit einer passenden WHERE-Klausel können wir die Auswahl auf die Computerteile beschränken, die noch nicht reserviert sind:

>>> cursor.execute(""" 
    SELECT fachnummer, komponente FROM lager WHERE reserviert=0 
""") 
>>> cursor.fetchall() 
[(1, u'Grafikkarte Typ 1'), (10, u'Netzteil Typ 3')]

Es können auch mehrere Bedingungen mittels logischer Operatoren wie AND und OR zusammengefasst werden. Damit könnten wir beispielsweise ermitteln, welche Artikel, die von der Firma »FiboComputing Inc.« geliefert wurden, schon reserviert worden sind:

>>> cursor.execute(""" 
    SELECT fachnummer, komponente FROM lager 
    WHERE reserviert!=0 AND lieferant='FC' 
""") 
>>> cursor.fetchall() 
[(25, u'LED-L\xfcfter')]

Da es lästig ist, immer die auszuwählenden Spaltennamen anzugeben, und man sehr oft Abfragen über alle Spalten machen möchte, gibt es dafür eine verkürzte Schreibweise, bei der die Spaltenliste durch ein Sternchen ersetzt wird:

>>> cursor.execute("SELECT * FROM kunden") 
>>> cursor.fetchall() 
[(12, u'Heinz Elhurg', u'Turnhallenstr. 1, 3763 Sporthausen'), 
(57, u'Markus Altbert', u'K\xe4mperweg 24, 2463 Duisschloss'), (64, u'Steve Apple', u'Podmacstr 2, 7467 Iwarhausen')]

Die Reihenfolge der Spaltenwerte richtet sich danach, in welcher Reihenfolge die Spalten der Tabelle mit CREATE definiert wurden.

Als letzte Ergänzung zum SELECT-Statement wollen wir uns mit den Abfragen über mehrere Tabellen, den sogenannten Joins (dt. Verbindungen), beschäftigen. Wir möchten zum Beispiel abfragen, welche Komponenten des Lieferanten mit der Telefonnummer 011235813 zurzeit im Lager vorhanden sind und in welchen Fächern sie liegen.

Eine Abfrage über mehrere Tabellen unterscheidet sich von einfachen Abfragen dadurch, dass anstelle des einfachen Tabellennamens eine durch Kommata getrennte Liste angegeben wird, die alle an der Abfrage beteiligten Tabellen enthält. Wenn auf Spalten, zum Beispiel in der WHERE-Bedingung, verwiesen wird, muss der jeweilige Tabellenname mit angegeben werden. Das gilt auch für die auszuwählenden Spalten direkt hinter SELECT. Unsere Beispielabfrage betrifft nur die Tabellen »Lager« und »Lieferanten« und lässt sich als Join folgendermaßen formulieren:

SELECT lager.fachnummer, lager.komponente, lieferanten.name 
FROM lager, lieferanten 
WHERE lieferanten.telefonnummer='011235813' AND 
      lager.lieferant=lieferanten.kurzname

Man kann sich die Verarbeitung eines solchen Joins so vorstellen, dass die Datenbank jede Zeile der Tabelle »Lager« mit jeder Zeile der Tabelle »Lieferanten« zu neuen Datensätzen verknüpft und aus der dadurch entstehenden Liste alle Zeilen zurückgibt, bei denen die Spalte lieferanten.telefonnummer den Wert '011235813' hat und die Spalten lager.lieferant und lieferanten.kurzname übereinstimmen.

Führen wir die Abfrage mit SQLite aus, erhalten wir die erwartete Ausgabe:

>>> sql = """ 
SELECT lager.fachnummer, lager.komponente, lieferanten.name 
FROM lager, lieferanten 
WHERE lieferanten.telefonnummer='011235813' AND 
      lager.lieferant=lieferanten.kurzname""" 
>>> cursor.execute(sql) 
>>> cursor.fetchall() 
[(1, u'Grafikkarte Typ 1', u'FiboComputing Inc.'), 
 (10, u'Netzteil Typ 3', u'FiboComputing Inc.'), 
 (25, u'LED-L\xfcfter', u'FiboComputing Inc.')]

Bis hierher haben wir nach einer Abfrage immer mit cursor.fetchall direkt alle Ergebnisse der Abfrage aus der Datenbank geladen und dann gesammelt ausgegeben. Diese Methode eignet sich allerdings nur für relativ kleine Datenmengen, da erstens das Programm so lange warten muss, wie die Datenbank noch alle Ergebnisse ermittelt und zurückgibt, und zweitens das Resultat komplett als Liste im Speicher gehalten wird. Dass dies bei sehr umfangreichen Ergebnissen eine Verschwendung von Speicherplatz darstellt, bedarf keiner weiteren Erklärung. Aus diesem Grund gibt es die Möglichkeit, die Daten zeilenweise, also immer in kleinen Portionen, abzufragen. Wir erreichen durch dieses Vorgehen, dass wir nicht mehr auf die Berechnung der kompletten Ergebnismenge warten müssen, sondern schon währenddessen mit der Verarbeitung beginnen können. Außerdem müssen nicht mehr alle Datensätze zeitgleich im Arbeitsspeicher verfügbar sein.

Mit der Methode fetchone der cursor-Klasse können wir jeweils ein Ergebnis-Tupel anfordern. Wurden bereits alle Datensätze der letzten Abfrage ausgelesen, gibt fetchone None zurück. Damit lassen sich auch große Datenmengen speichereffizient auslesen, auch wenn unser Beispiel mangels einer großen Datenbank nur drei Zeilen ermittelt:

>>> cursor.execute("SELECT * FROM kunden") 
>>> row = cursor.fetchone() 
>>> while row: 
        print row 
        row = cursor.fetchone() 
(12, u'Heinz Elhurg', u'Turnhallenstr. 1, 3763 Sporthausen') 
(57, u'Markus Altbert', u'K\xe4mperweg 24, 2463 Duisschloss') 
(64, u'Steve Apple', u'Podmacstr 2, 7467 Iwarhausen')

Diese Methode führt durch die while-Schleife zu etwas holprigem Code und wird deshalb seltener eingesetzt. Eine wesentlich elegantere Methode bietet die Iterator-Schnittstelle der cursor-Klasse, die es uns erlaubt, wie bei einer Liste mithilfe von for über die Ergebniszeilen zu iterieren:

>>> for row in cursor: 
        print row 
(12, u'Heinz Elhurg', u'Turnhallenstr. 1, 3763 Sporthausen') 
(57, u'Markus Altbert', u'K\xe4mperweg 24, 2463 Duisschloss') 
(64, u'Steve Apple', u'Podmacstr 2, 7467 Iwarhausen')

Aufgrund des wesentlich besser lesbaren Programmtextes ist die Iterator-Methode für solche Anwendungen der Methode fetchone vorzuziehen. Sie sollten fetchone nur dann benutzen, wenn Sie gezielt jede Ergebniszeile separat und auf eine andere Weise verarbeiten wollen.

Der Umgang mit Datentypen bei SQLite

Wie Ihnen sicherlich schon aufgefallen ist, gibt SQLite beim Abfragen von Daten für TEXT-Spalten immer unicode-Instanzen zurück, auch wenn beim Schreiben der Daten ursprünglich eine str-Instanz an die Datenbank übergeben wurde. Aus dem einleitenden Teil dieses Abschnitts kennen Sie bereits das Schema, nach dem SQLite Daten beim Schreiben der Datenbank konvertiert. Die entsprechende Rückübersetzung von SQLite-Datentypen zu Python-Datentypen wird durch folgende Tabelle beschrieben:


Tabelle 19.7  Typumwandlung beim Lesen von SQLite-Datenbanken
SQLite-Datentyp Python-Datentyp

NULL

None

INTEGER

int oder long, je nachdem, ob der Wertebereich von int ausreicht oder nicht.

REAL

float

TEXT

unicode

BLOB

buffer


Im Wesentlichen wirft diese Tabelle nur zwei Fragen auf: Wie speichere ich andere Datentypen, beispielsweise Listen oder meine eigenen Klassen, in der Datenbank, wenn doch nur diese Typen unterstützt werden? Und wie kann ich erreichen, dass ich für TEXT-Spalten anstatt unicode- wieder str-Instanzen erhalte?

Wir werden zuerst die zweite Frage beantworten.

Connection.text_factory

Jede von sqlite3.connect erzeugte Connection-Instanz hat ein Attribut text_factory, das eine Referenz auf eine Funktion enthält, die immer dann aufgerufen wird, wenn TEXT-Spalten ausgelesen werden. Im Ergebnistupel der Datenbankabfrage steht dann der Rückgabewert dieser Funktion. Standardmäßig ist das text_factory-Attribut auf die Built-in Function unicode gesetzt, was auch erklärt, warum immer unicode-Instanzen zurückgegeben werden:

>>> connection = sqlite3.connect("lagerverwaltung.db") 
>>> connection.text_factory 
<type 'unicode'>

Um unser Ziel zu erreichen, str-Instanzen für TEXT-Spalten zu erhalten, können wir eine eigene text_factory-Funktion angeben. Diese Funktionen müssen einen Parameter erwarten und den konvertierten Wert zurückgeben. Im Falle der str-Instanzen brauchen wir uns noch nicht einmal eine eigene Funktion dafür zu schreiben, da die Built-in Function str bereits alle Kriterien erfüllt. Damit können wir unsere Kundendaten folgendermaßen direkt als Byte-Strings ermitteln:

>>> connection.text_factory = str 
>>> cursor = connection.cursor() 
>>> cursor.execute("SELECT * FROM kunden") 
>>> cursor.fetchall() 
[(12, 'Heinz Elhurg', 'Turnhallenstr. 1, 3763 Sporthausen'), 
 (57, 'Markus Altbert', 'K\xc3\xa4mperweg 24, 2463 Duisschloss'), 
 (64, 'Steve Apple', 'Podmacstr 2, 7467 Iwarhausen')]

Beachten Sie hierbei, dass sqlite3 alle Sonderzeichen mit UTF-8 kodiert.

Connection.row_factory

Ein ähnliches Attribut wie text_factory für TEXT-Spalten existiert auch für ganze Zeilen. In dem Attribut row_factory kann eine Referenz auf eine Funktion gespeichert werden, die Zeilen für das Benutzerprogramm aufbereitet. Standardmäßig wird die Funktion tuple benutzt. Wir wollen beispielhaft eine Funktion implementieren, die uns auf die Spaltenwerte eines Datensatzes über die Namen der jeweiligen Spalten zugreifen lässt. Das Ergebnis soll dann folgendermaßen aussehen:

>>> cursor.execute("SELECT * FROM kunden") 
>>> cursor.fetchall() 
[{'anschrift': 'Turnhallenstr. 1, 3763 Sporthausen', 'kundennummer': 12, 'name': 'Heinz Elhurg'}, 
 {'anschrift': 'K\xc3\xa4mperweg 24, 2463 Duisschloss', 
 'kundennummer': 57, 'name': 'Markus Altbert'}, 
 {'anschrift': 'Podmacstr 2, 7467 Iwarhausen', 'kundennummer': 64, 
 'name': 'Steve Apple'}]

Um dies zu bewerkstelligen, benötigen wir noch das Attribut description der Cursor-Klasse, das uns Informationen zu den Spaltennamen der letzten Abfrage liefert. description enthält dabei eine Sequenz, die für jede Spalte ein Tupel mit sieben Elementen bereitstellt, von denen uns aber nur das erste, nämlich der Spaltenname, interessiert:

>>> cursor.execute("SELECT * FROM kunden") 
>>> cursor.description 
(('kundennummer', None, None, None, None, None, None), 
 ('name', None, None, None, None, None, None), 
 ('anschrift', None, None, None, None, None, None))

Die row_factory-Funktionen erhalten als Parameter eine Referenz auf den Cursor, der für die Abfrage verwendet wurde, und die Ergebniszeile als Tupel.

Mit diesem Wissen können wir unsere row_factory-Funktion namens zeilen_dict wie folgt implementieren:

def zeilen_dict(cursor, zeile): 
    ergebnis = {} 
    for spaltennr, spalte in enumerate(cursor.description): 
        ergebnis[spalte[0]] = zeile[spaltennr] 
    return ergebnis

Zur Erinnerung: enumerate erzeugt einen Iterator, der für jedes Element der übergebenen Sequenz ein Tupel zurückgibt, das den Index des Elements in der Sequenz und seinen Wert enthält.

In der Praxis arbeitet unsere row_factory wie folgt:

>>> connection.row_factory = zeilen_dict 
>>> cursor = connection.cursor() 
>>> cursor.execute("SELECT * FROM kunden") 
>>> cursor.fetchall() 
[{'anschrift': 'Turnhallenstr. 1, 3763 Sporthausen', 'kundennummer': 12, 'name': 'Heinz Elhurg'}, 
 {'anschrift': 'K\xc3\xa4mperweg 24, 2463 Duisschloss', 
 'kundennummer': 57, 'name': 'Markus Altbert'}, 
 {'anschrift': 'Podmacstr 2, 7467 Iwarhausen', 'kundennummer': 64, 
 'name': 'Steve Apple'}]

Pythons sqlite3-Modul liefert schon eine erweiterte row_factory namens sqlite3.Row mit, die die Zeilen ihn ähnlicher Weise verarbeitet wie unsere zeilen_dict-Funktion. Da sqlite3.Row sehr stark optimiert ist und außerdem der Zugriff auf die Spaltenwerte über deren Namen unabhängig von Groß- und Kleinschreibung erfolgen kann, sollten Sie die eingebaute Funktion unserem Beispiel vorziehen und nur dann eine eigene row_factory implementieren, wenn Sie etwas ganz anderes erreichen möchten.

Nach diesem kleinen Ausflug zu den factory-Funktionen wenden wir uns endlich der ersten unserer beiden Fragen zu: Wie können wir beliebige Datentypen in SQLite-Datenbanken speichern?

Adapter und Konvertierer

Wie Sie bereits wissen, unterstützt SQLite nur eine beschränkte Menge von Datentypen. Als Folge davon müssen wir alle anderen Datentypen, die wir in der Datenbank ablegen möchten, durch die vorhandenen abbilden. Aufgrund ihrer Flexibilität eignen sich die TEXT-Spalten am besten, um beliebige Daten aufzunehmen, weshalb wir uns im Folgenden auf diese beschränken.

Analog zur String-Kodierung, bei der wir unicode-Instanzen mittels ihrer encode-Methode in gleichwertige str-Instanzen umformen und die ursprünglichen Unicode-Daten mithilfe der decode-Methode wiederherstellen konnten, brauchen wir nun Operationen, um beliebige Datentypen erst in Strings zu transformieren und anschließend die Ursprungsdaten wieder aus dem String zu extrahieren.

Das Umwandeln von beliebigen Datentypen in einen String wird Adaption genannt, und die Rückgewinnung der Daten aus diesem String heißt Konvertierung. Abbildung 19.4 veranschaulicht diesen Zusammenhang am Beispiel der Klasse Kreis, die als Attribute die Koordinaten des Kreismittelpunktes Mx und My sowie die Länge des Radius R besitzt:

Abbildung 19.4  Schema der Adaption und Konvertierung

Eine entsprechende Kreis-Klasse lässt sich folgendermaßen definieren:

class Kreis(object): 
    def __init__(self, mx, my, r): 
        self.Mx = mx 
        self.My = my 
        self.R = r

Nun müssen wir eine Adapterfunktion erstellen, die aus unseren Kreis-Instanzen Strings macht. Die Umwandlung nehmen wir so vor, dass wir einen String erstellen, der durch Semikola getrennt die drei Attribute des Kreises enthält:

def kreisadapter(k): 
    return "%f;%f;%f" % (k.Mx, k.My, k.R)

Damit die Datenbank weiß, dass wir die Kreise mit dieser Funktion adaptieren möchten, muss sie registriert und mit dem Datentyp Kreis verknüpft werden. Dies geschieht durch den Aufruf der sqlite3.register_adapter-Methode, die als ersten Parameter den zu adaptierenden Datentyp und als zweiten Parameter die Adapterfunktion erwartet:

>>> sqlite3.register_adapter(Kreis, kreisadapter)

Durch diese Schritte ist es uns möglich, Kreise in TEXT-Spalten abzulegen. Wirklich nützlich wird das Ganze aber erst dann, wenn beim Auslesen auch automatisch wieder Kreis-Instanzen generiert werden.

Deshalb müssen wir noch die Umkehrfunktion von kreisadapter, den Konverter, definieren, der aus dem String die ursprüngliche Kreis-Instanz wiederherstellt. In unserem Beispiel erweist sich das als sehr einfach:

def kreiskonverter(string): 
    mx, my, r = string.split(";") 
    return Kreis(float(mx), float(my), float(r))

Genau wie der Adapter muss auch die Konverterfunktion bei SQLite registriert werden, was mit der Methode sqlite3.register_converter() erreicht wird:

>>> sqlite3.register_converter("KREIS", kreiskonverter)

Anders als register_adapter erwartet register_convert dabei einen String als ersten Parameter, der dem zu konvertierenden Datentyp einen Namen innerhalb von SQLite zuweist. Dadurch haben wir einen neuen SQLite-Datentyp namens KREIS definiert, den wir genau wie die eingebauten Typen für die Spalten unserer Tabellen verwenden können. Allerdings müssen wir SQLite beim Verbinden zu der Datenbank mitteilen, dass wir von uns definierte Typen verwenden möchten. Dazu übergeben wir der connect-Methode einen entsprechenden Wert als Schlüsselwortparameter detect_types:

>>> connection = sqlite3.connect(":memory:", 
                            detect_types=sqlite3.PARSE_DECLTYPES)

Nachfolgend wird die Definition und Verwendung unseres neuen Datentyps kreis in einem Miniprogramm demonstriert:

import sqlite3 
 
class Kreis(object): 
    def __init__(self, mx, my, r): 
        self.Mx = mx 
        self.My = my 
        self.R = r 
 
def kreisadapter(k): 
    return "%f;%f;%f" % (k.Mx, k.My, k.R) 
 
def kreiskonverter(string): 
    mx, my, r = string.split(";") 
    return Kreis(float(mx), float(my), float(r)) 
 
# Adapter und Konverter registrieren 
sqlite3.register_adapter(Kreis, kreisadapter) 
sqlite3.register_converter("KREIS", kreiskonverter) 
 
# Hier wird eine Beispieldatenbank im Arbeitsspeicher mit 
# einer einspaltigen Tabelle für Kreise definiert 
connection = sqlite3.connect(":memory:", 
                        detect_types=sqlite3.PARSE_DECLTYPES) 
cursor = connection.cursor() 
cursor.execute("CREATE TABLE kreis_tabelle(k KREIS)") 
 
# Kreis in die Datenbank schreiben 
kreis = Kreis(1, 2.5, 3) 
cursor.execute("INSERT INTO kreis_tabelle VALUES (?)", (kreis,)) 
 
# Kreis wieder auslesen 
cursor.execute("SELECT * FROM kreis_tabelle") 
 
gelesener_kreis = cursor.fetchall()[0][0] 
print type(gelesener_kreis) 
print gelesener_kreis.Mx, gelesener_kreis.My, gelesener_kreis.R

Die Ausgabe dieses Programms ergibt sich wie folgt und zeigt, dass gelesener_kreis tatsächlich eine Instanz unserer Kreis-Klasse mit den korrekten Attributen ist:

<class '__main__.Kreis'> 
1.0 2.5 3.0

Einschränkungen

Das Datenbanksystem SQLite ist in bestimmten Punkten eingeschränkt. Beispielsweise wird eine Datenbank beim Verändern oder Hinzufügen von Datensätzen für Lesezugriffe gesperrt, was besonders bei Webanwendungen sehr hinderlich ist: In der Regel werden mehrere Besucher eine Internetseite gleichzeitig aufrufen, und wenn jemand beispielsweise einen neuen Foreneintrag erstellt, wollen die anderen Besucher nicht länger auf die Anzeige der Seite warten müssen.

Deshalb gibt es andere Systeme, die auch mit den Anforderungen größerer Projekte zurechtkommen. Wir werden uns im nächsten Abschnitt mit dem verbreiteten Datenbankserver namens MySQL beschäftigen.


Galileo Computing - Zum Seitenanfang

19.3.2 MySQLdb  topZur vorigen Überschrift

MySQL ist ein Datenbankserver, der speziell für die schnelle Verwaltung sehr großer Datenmengen konzipiert wurde. Das System wird von der schwedischen Firma MySQL AB entwickelt und ist als quelloffene Software unter der GNU General Public License (GPL) verfügbar. Sie können sich die Software von der Homepage des Herstellers kostenlos herunterladen: http://www.mysql.com.

Für Internetanwendungen hat sich MySQL zum De-facto-Standard entwickelt, sodass heute praktisch jedes Hosting-Paket auch MySQL-Datenbanken enthält. Weil Python sich besonders für die Entwicklung von Webanwendungen eignet, haben wir uns entschlossen, an dieser Stelle die MySQL-Datenbank zu beschreiben, auch wenn sie nicht Teil der Python-Standardbibliothek ist.

Im Gegensatz zu SQLite, das sämtliche Funktionen der Datenbank in den Python-Interpreter kompiliert, wählt MySQL einen Client/Server-Ansatz. Das bedeutet, dass sich ein eigenes Programm, der sogenannte Server, um die Verwaltung der Daten kümmert. Die sogenannten Clients können sich mit diesem Server verbinden und ihre Abfragen senden bzw. Daten empfangen. Die Verbindungen zwischen Server und Client werden über Netzwerkschnittstellen aufgebaut, sodass der Datenbankserver nicht auf demselben Rechner laufen muss wie seine Clients. Außerdem können auf diese Weise viele Clients, die auf verschiedenen Computern laufen, denselben Datenbestand benutzen, ohne sich darum kümmern zu müssen, wo die Daten letztendlich physikalisch abgelegt sind.

Neben der größeren Flexibilität ermöglicht der Client/Server-Ansatz eine Rechteverwaltung für die Benutzer der Datenbank. Jeder Client muss sich beim Verbindungsaufbau zu dem Server mit Zugangsdaten einloggen. Der Server kann dann anhand der Benutzerdaten bestimmen, welche Aktionen der Client durchführen darf, oder den Verbindungsaufbau ganz ablehnen, wenn der übergebene Benutzer nicht existiert oder das Passwort falsch war.

Für den Zugriff auf die Datenbank muss der Client neben den Login-Daten nur die Netzwerkadresse des Datenbankservers kennen.

Sie können eine aktuelle Version des MySQLdb-Moduls für Python unter der Adresse http://sourceforge.net/projects/mysql-python/ herunterladen.

Nach der Installation können Sie das Modul MySQLdb mittels import einbinden:

>>> import MySQLdb

Nun können Sie mit der connect-Funktion eine Verbindung zu einem Datenbankserver herstellen. Wir gehen bei den folgenden Beispielen davon aus, dass ein MySQL-Server unter der Adresse "192.168.0.128" erreichbar ist und einen Benutzer namens "root" mit dem Passwort "123456" hat:

>>> connection = MySQLdb.connect("192.168.0.128", 
                                 "root", "123456", "test")

Der letzte Parameter mit dem Wert "test" gibt die Datenbank an, die wir auf dem Server verwenden möchten. MySQL ermöglicht es nämlich, mehrere Datenbanken auf demselben Server zu verwalten. Wir gehen davon aus, dass die Datenbank namens "test" bereits auf dem Server existiert, denn sie wird bei der Verbindung nicht automatisch erzeugt.

Nun können wir genau wie bei sqlite3 ein Cursor-Objekt für die Verbindung erzeugen und damit Anfragen an die Datenbank senden. Die Details zum Umgang mit Datenbankverbindungen und Cursor-Objekten können Sie im Abschnitt über SQLite nachlesen.

Zur Demonstration werden wir eine einfache Tabelle erzeugen, die die Marke, die Leistung und das Baujahr von Autos speichern kann:

import MySQLdb 
connection = MySQLdb.connect("www.hostname.de", 
                         "benutzername", "passwort", "datenbank") 
cursor = connection.cursor() 
cursor.execute(""" 
   CREATE TABLE autos (marke TEXT, ps INTEGER, baujahr INTEGER) 
    """) 
cursor.execute("INSERT INTO autos VALUES('Volvo', 130, 1995)") 
daten = (("Audi", 350, 2005), 
         ("Ford", 110, 2000), 
         ("VW", 60, 2001)) 
cursor.executemany("INSERT INTO autos VALUES(%s, %s, %s)", daten) 
 
cursor.execute("SELECT marke, ps, baujahr FROM autos") 
for row in cursor: 
    print "Marke: %s, Leistung: %d PS, Baujahr: %d" % row

Die Ausgabe des Programms sieht folgendermaßen aus:

Marke: Volvo, Leistung: 130 PS, Baujahr: 1995 
Marke: Audi, Leistung: 350 PS, Baujahr: 2005 
Marke: Ford, Leistung: 110 PS, Baujahr: 2000 
Marke: VW, Leistung: 60 PS, Baujahr: 2001

Im Unterschied zu sqlite3 verwendet MySQLdb bei Parameterübergaben nicht das Fragezeichen als Platzhalter, sondern die Zeichenfolge "%s". Wie im Beispiel gezeigt wird, können Sie "%s" auch für andere Datentypen als str verwenden. Das Modul MySQLdb kümmert sich dann um die entsprechende Umwandlung.

Anstelle von "%s" können Sie die Parameter auch mit Namen versehen und dann statt einer Sequenz ein Dictionary übergeben. Die folgenden beiden Aufrufe von execute erzeugen das gleiche Ergebnis:

>>> cursor.execute(""" 
    SELECT * FROM autos WHERE marke=%s AND ps=%s""", 
    ("Volvo", 130) 
    ) 
>>> cursor.execute(""" 
    SELECT * FROM autos 
    WHERE marke=%(marke)s AND ps=%(leistung)s""", 
    {"marke" : "Volvo", "Leistung" : 130} 
    )

Allerdings bietet MySQLdb keine komfortable Schnittstelle zum Speichern eigener Datentypen. Sie müssen sich in Ihren Programmen selbst darum kümmern, dass Ihre Objekte beim Speichern in einen String und beim Lesen wieder in den entsprechenden Datentyp konvertiert werden.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.






 <<   zurück
  
  Zum Katalog
Zum Katalog: Python






Python
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Linux






 Linux


Zum Katalog: Ubuntu GNU/Linux






 Ubuntu GNU/Linux


Zum Katalog: Praxisbuch Web 2.0






 Praxisbuch Web 2.0


Zum Katalog: UML 2.0






 UML 2.0


Zum Katalog: Praxisbuch Objektorientierung






 Praxisbuch Objektorientierung


Zum Katalog: Einstieg in SQL






 Einstieg in SQL


Zum Katalog: IT-Handbuch für Fachinformatiker






 IT-Handbuch für Fachinformatiker


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo