Konzeption und Realisierung einer Datenbank zur - RWTH

Werbung
Fachhochschule Aachen
Fachbereich 2 - Bauingenieurwesen
Seminararbeit im Studiengang Scientific Programming
Wintersemester 2011/2012
Betreuer:
Prof. Dr. Jürgen Höttges
Valerij Herber, B.Sc.
Konzeption und Realisierung einer Datenbank zur
Klausurenverwaltung mit PostgreSQL/Python
Daniel Lütkehermölle
Malteserstraße 20
52062 Aachen
[email protected]
16. Dezember 2011
Inhaltsverzeichnis
1 Einleitung
1.1 Ausgangssituation und Motivation . . . . . . . . . . . . . . . . . .
1.2 Konventionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3
3
3
2 Grundlagen
2.1 Programmiersprache Python
2.1.1 Allgemeines . . . . . .
2.1.2 PsycoPG . . . . . . .
2.2 Datenbanken . . . . . . . . .
2.2.1 Allgemeines . . . . . .
2.2.2 SQL . . . . . . . . . .
2.2.3 Relationale Algebra .
4
4
4
5
5
5
7
7
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3 Entwurf des Datenbankschemas
4 Umsetzung
4.1 Verwendete Software
4.2 Datenbank . . . . . .
4.3 Datenmigration . . . .
4.4 Python Schnittstellen .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
9
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
11
11
11
12
12
5 Fazit
14
6 Ausblick
14
7 Anhang
7.1 Abbildungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.2 Quelltextbeispiele . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.3 Literaturverzeichnis . . . . . . . . . . . . . . . . . . . . . . . . . .
15
15
16
27
1 Einleitung
1.1 Ausgangssituation und Motivation
Im Fachbereich 2 der Fachhochschule Aachen sind die Raumkapazitäten für
Klausuren begrenzt. Um eine optimale Auslastung dieser Räumlichkeiten zu erreichen, ist eine sorgfältige Planung notwendig. Dabei spielen viele Faktoren eine
Rolle, wie zum Beispiel die Anzahl der Studenten oder Krankheitsfälle unter Aufsichtsführenden, die sich auch kurzfristig noch ändern können und eine schnelle
Änderung der Planung notwendig machen. Um diesen Planungsaufwand zu vereinfachen, wurde ein Datenbanksystem mit einer einfachen Benutzerschnittstelle
entwickelt, die dabei helfen soll die Organisation von Klausuren durchzuführen
und die Raumverteilung in Form von Papieraushängen zu publizieren. Da jedoch nicht viel Zeit in dieses System investiert wurde, erschien es an vielen Stellen unvollständig und verbesserungswürdig. Mit dieser Arbeit soll dieses System
von Grund auf neu entwickelt werden. Dabei wurden auch durch Besprechungen
mit dem Nutzerkreis gewünschte Funktionalitäten ergründet, die im alten System
noch nicht umgesetzt waren.
1.2 Konventionen
• Im Zusammenhang dieser Arbeit wird das Wort „Sprache“ synonym zu „Programmiersprache“ verwendet.
• Diagramme der Datenbank sind gemäß des UML-Standards angefertigt.
• Quellenangaben und Anmerkungen im Text werden in nummerierten Fußnoten angegeben.
• Quelltextbeispiele werden durch eine andere Schriftart und Hervorhebung
der Syntax gesondert gekennzeichnet.
3
2 Grundlagen
2.1 Programmiersprache Python
2.1.1 Allgemeines
Die Programmiersprache Python wurde um 1990 mit dem Ziel entwickelt einfach
benutzbar und lesbar zu sein und dabei dennoch eine umfangreiche Funktionalität zu besitzen1 Es handelt sich bei Python vordergründig um eine imperative Sprache, hält aber mittlwerweile auch objektorientierte Ansätze vor. So ist
das Konzept der Polymorphie und Vererbung in Python umsetzbar. Die Möglichkeit mit verschiedenen Programmierparadigmen arbeiten zu können, macht Python zu einer sehr vielseitigen Sprache. Da unsere Abteilung hauptsächlich aus
Auszubildenden und wiss. Hilfskräften besteht, gibt es eine hohe Fluktuation an
Mitarbeiten, deren Beteiligung an Programmierprojekten selten drei Jahre überschreiten. Daher ist es wichtig, dass sich ein Programmierer leicht in bestehende
Projekte einarbeiten kann, was nur durch gut lesbaren und dokumentierten Quelltext erleichtert werden kann. Python bringt die Eigenschaft von Haus aus mit und
unterstützt schon beim Programmieren die Lesbarkeit durch die fest vorgegebene
Syntax. Der Personalstruktur dienlich ist auch die aufgrund der geringen Anzahl
von Schlüsselwörtern und der impliziten Typisierung von Datenstrukturen schnelle Erlernbarkeit für Programmieranfänger im Vergleich zu Programmiersprachen
wie C/C++ oder Java. Ein weiterer Vorteil sind die Plattformunabhängigkeit, die
die Sprache durch den Python-Interpreter erlangt, und die Unterstützung unterschiedlicher Programmierparadigmen, die es ermöglichen Python in einer Vielzahl von Projekten verwenden zu können.
Schlussendlich bieten die Lizenzen, unter denen Python (PSF-Lizenz2 und die
Bibliothek PsycoPG (LGPL-Lizenz) 3 entwickelt und veröffentlicht werden, die
Möglichkeit einer freien Verwendung ohne rechtliche Einschränkungen. In dieser Arbeit wird Python in der Version 2.7.2 benutzt, welche die letzte Version aus
dem Entwicklungsbaum von Python 2 darstellt. Das aktuellere Python 3 wurde
nicht verwendet, da zum Zeitpunkt der Programmierung einige Bibliotheken (z.B.
PsycoPG) noch nicht für die Version 3 umgesetzt waren.
1
Peter Kaiser, Johannes Ernesti: Python - Das umfassende Handbuch,Galileo Computing,Bonn,2008:S.28
2
http://docs.python.org/license.html, Dezember 2011
3
http://initd.org/psycopg/license/, Dezember 2011
4
2.1.2 PsycoPG
Für die Anbindung an die Datenbank wird die Python-Bibliothek PsycoPG verwendet, die eine sehr komfortable Schnittstelle für SQL-Abfragen an die Datenbank bietet.
2.2 Datenbanken
2.2.1 Allgemeines
Moderne Informationsverarbeitungssystem sind ohne ein DBMS (Datenbankmanagementsystem) kaum noch denkbar. Gegenüber einer Datenhaltung in einem
einfachen Dateisystem hat die Datenbank erhebliche Vorteile. Informationen in
Datenbanken lassen sich leicht in neue Relationen setzen, was mit isolierten Dateien nur unter Aufwand möglich ist und bietet so auch in bereits bestehenden
System die Möglichkeit existierende Daten in vorher ungeplanten Zusammenhang zu stellen.
Ein DBMS besteht aus drei Schichten:
• Physische Ebene - Hier wird die eigentliche Datenspeicherung auf dem
Datenträger (z. B. Festplatte) organisiert.
• Logische Ebene - Diese Ebene beherbergt das Datenbankschema, in der
die Daten in einen logischen Bezug gebracht werden. Das sogenannte Datenmodell versucht dabei den realen Bezug möglichst genau abzubilden.
• Sichtebene - Die Schnittstelle zu den Benutzergruppen wird über - von
der physischen Datenspeicherung nun völlig unabhängigen - Programme
geleistet. Diese können die Informationen des Datenbankschemas auf die
Wünsche und Rechte des jeweilgen Nutzers angepasst darstellen.
Damit ein DBMS die oben beschriebenen Vorteile bietet, müssen die darauf ausführbaren Transaktionen die sogenannten ACID (dt. AKID)-Eigenschaften erfüllen. AKID ist die Abkürzung für die vier Eigenschaften, die eine Datenbank erfüllen muss, um überhaupt funktional zu sein und machen das aus, was sie von
anderen Datenhaltungssystemen, wie der Speicherung in einem Dateisystem,
abhebt.
5
• Atomizität - Diese Eigenschaft verlangt, dass eine Transaktion als kleinste,
nicht mehr weiter zerlegbare Einheit behandelt wird. [. . . ]„alles-oder-nichts“Prinzip
• Konsistenz - Eine Transaktion hinterlässt nach Beendigung einen konsistenten Datenbasisbestand. Andernfalls werden sie komplett [. . . ] zurückgesetzt. [. . . ] Der resultierende Endzustand muss die im Schema definierten
Konsistenzbedigungen (z.B. referentielle Integrität) erfüllen.
• Isolation - Nebenläufig [. . . ] ausgeführte Transaktionen [dürfen] sich nicht
gegenseitig beeinflussen. Jede Transaktion muss - logisch gesehen - so
ausgeführt werden, als wäre sie die einzige Transaktion. [. . . ]
• Dauerhaftigkeit - Die Wirkung einer erfolgreich abgeschlossenen Transaktion bleibt dauerhaft in der Datenbank erhalten. 4
Die Eigenschaften der Dauerhaftigkeit wird werden bereits durch die eingesetzte
PostgreSQL-Software sichergestellt und der Datenbankentwickler muss sich darum in der Regel nicht selbst sorgen. Ebenfalls kann SQL vor einer Inkonsistenz
der Daten schützen, indem die Auswertung von Anfragen an die Datenbank, die
eine Inkonsistenz hervorrufen würden, nicht ausgeführt werden. So warnt z.B.
PostgreSQL bevor eine Spalte, die als Fremdschlüssel zu anderen Datensätzen
verweist gelöscht werden soll und lässt die Löschung erst zu, wenn diese Abhängigkeit zuvor gelöst wurde. Atomizität und Isolation kann von SQL allerdings nur
bedingt überprüfen und gewährleisten. Der Entwickler muss darauf achten, dass
sein Datenbankkonzept diese Eigenschaften erfüllt, indem er sein entwickeltes
Schema schrittweise normalisiert. Dabei helfen die sogenannten Normalformen.
Normalformen sind auf einander aufbauende formale Anforderungen an ein Datenbankschema, die die Datenredundanz immer weiter verringern ohne dabei formal mathematisch bewiesen - zu Datenverlust zu führen. Insgesamt sind sechs
Normalformen in der Literatur bekannt, allerdings haben sowohl die Boyce-CoddNormalform (BCNF), als auch die vierte und fünfte Normalform (4NF, 5NF) einen
speziellen Charakter und werden nicht erläutert, da sie in dem später gezeigten
Datenbankschema keine Verwendung finden.5 Auf die ersten drei Normalformen
soll in der folgenden Tabelle eingegangen werden, in der zu jeder Normalform
eine kurze Definition und die positiven Folgen für eine so normalisierte Tabelle
skizziert sind.
4
Alfons Kemper, Andre Eickler: Datenbanksysteme - Eine Einführung, Oldenbourg Verlag,München,2009:S.283
5
http://de.wikipedia.org/wiki/Normalisierung_(Datenbank), Dezember 2011
6
Normalform
Definition
Resultat
1. Normalform
(1NF)
Alle Attribute müssen einen
atomaren Wertebereich besitzen.
Die Atomizität ist nun erfüllt.
2. Normalform
(2NF)
Nichtschlüsselattribute dürfen nur von einem einzigen
Schlüssel direkt abhängen.
Die Abhängigkeiten zwischen Attributen ist auf die
logisch Nachvollziehbaren
beschränkt. Die Tabelle
erfüllt aber noch nicht
alle Anforderungen der
Isolationsbedingung.
3. Normalform
(3NF)
Nichtschlüsselattribute dürfen nicht transitiv von einem
Schlüssel abhängen.
Die Tabelle besitzt nun die
Eigenschaft der Isolation.
2.2.2 SQL
SQL steht für „Structured Query Language“ und wurde in den 70er Jahren des
letzten Jahrhunderts bei IBM speziell dazu entwickelt Abfragen an Datenbanksystem stellen zu können. Das Programmierparadigma ist deklarativ. Da es sich
bei SQL nicht um eine turingvollständige Sprache handelt, also nicht beliebige
Programme damit entwickelt werden können, ist es oft nötig sie mit anderen Programmiersprachen (wie in diesem Fall mit Python) zu kombinieren.
SQL ist eine reine Anragesprache, es ist also für den Entwickler bei der Programmierung mit SQL unwichtig, wie die Daten zusammengestellt werden, sondern
nur welche Daten er erhält. Die vorher erwähnten relationalen Algebraoperation
können mit dieser Sprache umgesetzt werden.
Eine Besonderheit der Sprache ist, dass verschiedene Hersteller den SQL-Standard
sehr unterschiedlich umsetzen. So ist es z.B. nicht direkt möglich Anfragen aus
MySQL in PostgreSQL zu übertragen. Zwar sind grundlegende Abfragen in allen
Derivaten des SQL-Standards grundsätzlich gleich, doch kann sich die Syntax
und auch fortgeschrittenere Befehle in den Umsetzungen so stark unterscheiden,
dass keine Kompatibilität untereinander mehr vorhanden ist.
2.2.3 Relationale Algebra
Um später den Datenbestand sinnvoll nutzen zu können, muss man die Datenabfragen mathematisch sinnvoll gestalten können. Dafür werden hier verschiedene
7
mathematische Funktionen vorgestellt mit denen das durchgeführt werden kann.
Es handelt sich dabei um die fünf Grundfunktionen, aus denen sich weitere Funktionen ableiten lassen, die an dieser Stelle aber nicht erwähnenswert sind.
• Selektion
Es werden Zeilen ausgegeben, die eine bestimmte Bedingung erfüllen.
SQL-Beispiel:
SELECT ∗ FROM [ T a b e l l e ]
2 WHERE [ Bedingung
erfüllt ];
1
• Projektion
Es wird die Auswahl an Spalten ausgegeben.
SQL-Beispiel:
1
SELECT [ S p a l t e ] FROM [ T a b e l l e ]
• Vereinigung
Zwei Relationen mit verknüpften Schlüssel werden zusammengefast ausgegeben.
SQL-Beispiel:
1
2
3
SELECT ∗ FROM [ T a b e l l e 1 ]
UNION
SELECT ∗ FROM [ T a b e l l e 2 ]
• Mengendifferenz
Es werden alle Zeilen ausgegeben, deren Schlüssel in der ersten, aber nicht
in der zweiten Tabelle vorkommen.
SQL-Beispiel:
1
2
3
SELECT ∗ FROM [ T a b e l l e 1 ]
EXCEPT
SELECT ∗ FROM [ T a b e l l e 2 ]
• kartesisches Produkt
Es werden ohne Einschränkung alle möglilchen Paare aus Tabelle 1 und
Tabelle 2 gebildet.
SQL-Beispiel:
1
SELECT ∗ FROM [ T a b e l l e 1 ] , [ T a b e l l e 2 ]
8
3 Entwurf des Datenbankschemas
Der Entwurf erfolgte nach der „top-down“-Vorgehensweise, in der ein Schema in
mehreren aufeinanderfolgenden Entwurfsschritten entwickelt wird. Nach jedem
Schritt wird der Prototyp mit den Anforderungen an das Schema verglichen und
im nächsten Schritt erneut darauf angepasst.
Im ersten Zyklus wurde in der Anforderungsanalyse geprüft, welche Entitäten für
die Datenbank relevant sind. Das hat die notwendigen Entitäten „spec“ (Vertiefungsrichtung), „course“ (Studiengang), „module“ (Fach), „Date“ (Datum), „room“
(Raum), „examiner“ (Prüfer) und „Aufsichtsperson“ ergeben. Diesen Entitäten fügt
man in einem zweiten Schritt beschreibende Attribute zu. „Prüfer“ und „Aufsichtsperson“ benötigen z.B. eine Peronalnummer zur internen Identifikation und einen
Namen. Es war aufgrund der identischen Attributsmenge also möglich diese beiden Entitäten unter „Person“ zusammenzufassen.
Auf diese Weise wurden alle Entitäten des Modells ermittelt und sie können nun
zueinander in Beziehung gesetzt werden.
• Beziehung „part of“: Ein Fach ist immmer genau einem Studiengang zugeordnet, ein Studiengang kann allerdings auf mehrere Fächer verweisen.
Im ER-Diagramm wird diese Beziehung als 1:N-Beziehung bezeichnet. Außerdem kann ein Fach nur dann geben, wenn es in einem Studiengang
angeboten wird. „Fach“ ist eine sogenannte existenzabhängige Entität.
• Beziehung „has a“: Jedes Fach gehört zu einer Vertiefungsrichtung, es
können mehere Fächer zu einer Vertiefungsrichtung gehören.
• Beziehung „examines“: Es gibt für ein Fach genau eine Person (den Prüfer), allerdings kann es noch ein oder mehrere Zweitprüfer geben, daher
wird diese Beziehung als 1:N-Beziehung dargestellt.
• Beziehung „when“: 1:N-Beziehung. An einem Datum können mehrere
Klausuren geschrieben werden, aber eine Klausur nur an einem Datum (pro
Prüfungsperiode).
• Beziehung „where“: Hier wird darauf Rücksicht genommen, dass durchaus verschiedene Klausuren in einem Raum statt finden können, als auch
die Teilnehmer einer Klausur auf mehrere Räume verteilt werden können.
Hier handelt es sich um eine N:M-Beziehung.
• Beziehung „supervise“: Jeweils eine Person reicht aus, um einen Raum
zu betreuen. Mehr als eine 1:1-Beziehung ist hier also nicht notwendig.
9
In Abb. 1 ist das aus diesen Entitäten und Beziehungen entwickelte Entity-RelationshipDiagram zu sehen.
10
4 Umsetzung
4.1 Verwendete Software
• GNU/Linux Debian Squeeze 6.0.2 (Lizenz: div. DFSG konforme Lizenzen6 )
• PostgreSQL 8.4.9 (Lizenz: PostgreSQL License 7 )
• Git 1.7.5.4 (Lizenz: GPL 3.0 8 )
• Python 2.7.2+ (Lizenz: PSF License 9 )
• PsycoPG 2.4.2 (Lizenz: LGPL License 10 )
• PGAdmin3 1.12.3 (Lizenz: PostgreSQL License)
4.2 Datenbank
Auf einer mit dem Betriebssystem Debian betriebenen virtuellen Maschine wurde mit PostgreSQL eine Datenbank erstellt. Mittels PGAdmin3 wurde das vorher
beschriebene Datenbankschema implementiert.
In Abb. 2 ist das endgültig implementierte Schema zu sehen. Alle Entitäten und
Relationen, die bereits in der Konzeption angedacht waren sind auch hier wieder
als solche zu erkennen.
Die Tabellen „course“, „date“, „room“, „person“ und „spec“ sollen hierals primäre
Tabellen bezeichnet werden, da sie ohne einen Primärschlüssel auskommen.
Dies sind die Entsprechungen der Entitäten aus dem vorherigen Kapitel. Die Tabelle „modul“ hat zwar auch eine Entität als Entsprechung, ist aber noch von den
Tabellen „course“ und „spec“ abhängig. Hier wurde auf eine Normalisierung vorerst verzichtet, um die Datenmigration von der alten Datenbank nicht unnötig zu
erschweren (vgl. Kap.4.3). Die Relationen „part of“ und „has a“ haben hier keine
entsprechenden Tabellen.
Eine ebensolche Vereinfachung wurde auch bei der Relationen „supervise“ und
„when"gemacht, die als Tabelle neben den beiden Fremdschlüsseln, die „room“
mit „person“ verknüpfen sollen, noch einen Primärschlüssel besitzen. „supervised_exam“ als Entsprechung zu „supervise“ braucht diesen Primärschlüssel,
6
http://wiki.debian.org/DFSGLicenses (Stand: Dez. 2011)
http://www.postgresql.org/about/licence/ (Stand: Dez. 2011)
8
http://www.gnu.org/licenses/gpl-3.0.html (Stand: Dez. 2011)
9
http://docs.python.org/license.html (Stand: Dez. 2011)
10
http://initd.org/psycopg/license/ (Stand: Dez. 2011)
7
11
um in der Tabelle „exam_room“ referenziert werden zu können. Die Tabelle „exam_date“ (für die Relation „when“) erhielt zusätzlich das Attribut „attendees“, um
die Prüfungsteilnehmer an einem bestimmten Klausurtermin aufnehmen zu können.
Die Tabelle „exam“ ist gänzlich neu hinzugekommen, da neue Attribute, „po_version“
(Prüfungsordnung) und „mode“ (Prüfungsart) hinzugekommen sind, die logisch
nicht noch dem „modul“ zugeschlagen werden konnten.
4.3 Datenmigration
Die Migration der Daten aus der alten MySQL Datenbank wurde erreicht, indem
zunächst mithilfe des MySQL Frontends PHPMyAdmin alle Inhalte mitsamt des
Schemas in eine zu PostgreSQL syntaktisch kompatible Form übersetzt und dann
als SQL-Dumpfile exportiert wurden. Diese Daten wurden dann in die PostgreSQL Datenbank importiert, so dass zunächst sowohl das alte, als auch das neue
Datenbankschema für kurze Zeit parallel in einer Datenbank liefen. Nun konnten
die Daten mit SQL-Abfragen aus dem alten Schema in das neue Schema übertragen werden. Wenn eine Datenbank nicht das Kriterium der ersten Normalform
einhält, wird dieses Unterfangen jedoch enorm erschwert. Der alte Datenbestand
enthielt sowohl Elemente, in denen mehrere Attribute den eindeutigen Schlüssel
bildeteten (Verletzung der zweiten Normalform) als auch mengenwertige Attributdaten (Verletzung der ersten Normalform). Dies führte dazu, dass die Abfragen
enorm komplex wurden.
4.4 Python Schnittstellen
Die Schnittstellen, die die Daten aus der Datenbank in Python zur Verfügung stellen sind in zwei Klassen aufgetrennt. Der „DBHandler“(Listing 2) sorgt für eine Anbindung zur Datenbank und ist für den Lesezugriff auf die Datenbank verantwortlich. Die GUI-Anwendung erhält Daten über die Methode „getList“ (Z.43f), der der
Name der jeweils gewünschten Tabelle übergeben werden muss. Die Ausgabe
erfolgt im Datentyp Dictionary. In jedem Element befindet sich dann eine Zeile der
Tabelle in der Form „[Spaltenname1]:[Feldinhalt1],[Spaltenname2]:[Feldinhalt2],. . . “,
die dann weiterverarbeitet werden können. Wird neben dem Lesezugriff auch
Schreibzugriff benötigt, so bietet die Klasse DBInputHandler (Listing 3 die Möglichkeit der Authentifizierung als Administrator (Z.27f).
Für jede Tabelle gibt es hier außerdem eine Methode, die als jeweilige Übergabeparameter die Eingabedaten für jeden Attributwert der Tabelle benötigen.
12
Intern stellt die Bibliothek PsycoDB die Methoden zur Verfügung, um die Eingabedaten in SQL Abfragen zu verwandeln.
Die Methode psycopg2.connect gibt eine Instanz der Klasse Connection zurück.
Diese Klasse erzeugt nun ihrerseits einen Object der Klasse Cursor, der sich nun
um das Handling der Datenbankabfragen kümmert. Mittels cursor.execute([SQLAbfrage]) ist es nun möglich direkt SQL-Abfragen per Python an die Datenbank
zu stellen und die Methode cursor.fetchall() gibt den Rückgabewert der letzten
Abfrage in Listenform aus. Umgekehrt können dem curser ebenfalls per Methode
exute() Daten in die Datenbank eingespeist werden. Statt einer normalen Abfrage übergibt man der Methode nun einfach deinen update- oder insert-Befehl. Die
Methoden fetch() und fetchall() geben nun entweder None oder einen Fehlercode
bei einem Fehlschlag zurück.
13
5 Fazit
Es stellte sich heraus, dass das Arbeiten mit Datenbanken vor allem darin eine
Herausforderung ist, die wirkliche Welt zu abstrahieren und möglichst gut in einem Beziehungsschema abbzubilden. Wie bei der Diskrepanz zwischen Theorie und Praxis üblich, gelingt das mal mehr und mal weniger erfolgreich. Oft
muss man Kompromisse eingehen, die mathematisch keine optimalen Lösungen darstellen, um zu gewährleisten, dass die Datenbank auch für diejenigen
noch schnell erfassbar bleibt, wenn sie neu in das Projekt hinzukommen (neue
Programmierer) oder mit der Thematik wenig vertraut sind (Kunden, Nutzer). Die
standardisierten Vorgehensweisen, die in der Literatur beschrieben sind, helfen
dabei ungemein einen roten Faden zu behalten und man sollte unbedingt ein solches Vorgehensmodell wählen, wenn man eine Datenbank entwickeln möchte.
Hier, wie auch in der
6 Ausblick
Wie wohl jedes Softwareprojekt ist auch dieses noch weit von Perfektion entfernt.
Eine konkrete Funktionalität, die möglich, aber noch nicht umgesetzt wurde, ist
ein automatisierter Datenimport aus dem QiS-System der Fachhochschule, das
die meisten Daten über Klausuren bereits vorhält. Bisher müssen diese Daten
noch von Hand in eine entsprechende GUI-Maske eingegeben werden, was unnötig Zeit und Mühe kostet. Es ist zu ergründen in wie weit diese Anbindung
auch in beide Richtungen funktionieren könnte, dann könnten Dozenten ihre Modul und Klausurdaten frei zwischen beiden Systemen austauschen. Sogar eine
Anmeldung am Qis-System über dieses System wäre denkbar, auch wenn dabei
sicherheits- und dsatenschutztechnische Aspekte geklärt werden müssen.
Eine weitere Möglichkeit ist die Erweiterung der Datenbasis, in dem weitere für
Studenten, Dozenten und Prüfer relevante Daten aufgenommen werden, wie z.B.
Hinweise zu Klausurvorbereitungen. Der Ausbau zu einer Raumverwaltung über
die Klausurzuteilung hinaus ist ebenfalls denkbar.
14
7 Anhang
7.1 Abbildungen
Abbildung 1: Entity-Relationship-Diagram des Datenbankkonzeptes
15
Abbildung 2: Das Schema der implementierten Datenbank
7.2 Quelltextbeispiele
Listing 1: Der Quelltext zur Erstellung des SQL-Datenschemas.
1
−− Table : course
2
3
−− DROP TABLE course ;
4
5
6
7
8
9
10
11
12
CREATE TABLE course
(
i d _ c o u r s e character varying ( 3 2 ) NOT NULL,
" name " character varying ( 1 2 8 ) NOT NULL,
degree character varying ( 3 2 ) NOT NULL,
CONSTRAINT course_pkey PRIMARY KEY ( i d _ c o u r s e )
)
WITH (
16
13
14
15
OIDS=FALSE
);
ALTER TABLE course OWNER TO p o s t g r e s ;
16
17
18
−− Table : date
19
20
−− DROP TABLE date ;
21
22
23
24
25
26
27
28
29
30
31
32
33
34
CREATE TABLE date
(
i d _ d a t e character varying ( 3 2 ) NOT NULL,
pp character varying ( 3 2 ) ,
due_date timestamp w i t h o u t time zone ,
d e a d l i n e timestamp w i t h o u t time zone ,
CONSTRAINT date_pkey PRIMARY KEY ( i d _ d a t e ) ,
CONSTRAINT date_check CHECK ( d e a d l i n e < due_date )
)
WITH (
OIDS=FALSE
);
ALTER TABLE date OWNER TO p o s t g r e s ;
35
36
37
−− Table : exam
38
39
−− DROP TABLE exam ;
40
41
42
43
44
45
46
47
48
49
CREATE TABLE exam
(
id_exam character varying ( 3 2 ) NOT NULL,
examiner1 character varying ( 3 2 ) NOT NULL,
examiner2 character varying ( 3 2 ) ,
modul character varying ( 3 2 ) NOT NULL,
" mode " character varying ( 3 2 ) NOT NULL,
p o _ v e r s i on character varying ( 3 2 ) NOT NULL,
CONSTRAINT exam_pkey PRIMARY KEY ( id_exam ) ,
17
50
51
52
53
54
55
56
57
58
59
60
61
62
63
CONSTRAINT exam_examiner1_fkey FOREIGN KEY ( examiner1 )
REFERENCES person ( i d _ pe r s o n ) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION ,
CONSTRAINT exam_examiner2_fkey FOREIGN KEY ( examiner2 )
REFERENCES person ( i d _ pe r s o n ) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION ,
CONSTRAINT exam_modul_fkey FOREIGN KEY ( modul )
REFERENCES modul ( id_modul ) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
ALTER TABLE exam OWNER TO p o s t g r e s ;
64
65
66
−− Table : exam_date
67
68
−− DROP TABLE exam_date ;
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
CREATE TABLE exam_date
(
id_exam_date character varying ( 3 2 ) NOT NULL,
exam character varying ( 3 2 ) NOT NULL,
a t t e n d e es i n t e g e r NOT NULL,
date character varying ( 3 2 ) NOT NULL,
CONSTRAINT exam_date_pkey PRIMARY KEY ( id_exam_date ) ,
CONSTRAINT exam_date_date_fkey FOREIGN KEY ( date )
REFERENCES date ( i d _ d a t e ) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION ,
CONSTRAINT exam_date_exam_fkey FOREIGN KEY ( exam )
REFERENCES exam ( id_exam ) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
18
87
ALTER TABLE exam_date OWNER TO p o s t g r e s ;
88
89
90
−− Table : exam_room
91
92
−− DROP TABLE exam_room ;
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
CREATE TABLE exam_room
(
exam_date character varying ( 3 2 ) ,
supervised_room character varying ( 3 2 ) ,
CONSTRAINT exam_room_exam_date_fkey FOREIGN KEY (
exam_date )
REFERENCES exam_date ( id_exam_date ) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION ,
CONSTRAINT exam_room_supervised_room_fkey FOREIGN KEY (
supervised_room )
REFERENCES supervised_room ( id_supervised_room ) MATCH
SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
ALTER TABLE exam_room OWNER TO p o s t g r e s ;
109
110
111
−− Table : modul
112
113
−− DROP TABLE modul ;
114
115
116
117
118
119
120
CREATE TABLE modul
(
id_modul character varying ( 3 2 ) NOT NULL,
" name " character varying ( 6 4 ) NOT NULL,
course character varying ( 3 2 ) NOT NULL,
d u r a t i o n double p r e c i s i o n ,
19
121
122
123
124
125
126
127
128
129
130
131
132
133
134
" comment " character varying ( 3 2 ) ,
spec character varying ( 3 2 ) ,
CONSTRAINT modul_pkey PRIMARY KEY ( id_modul ) ,
CONSTRAINT modul_course_fkey FOREIGN KEY ( course )
REFERENCES course ( i d _ c o u r s e ) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION ,
CONSTRAINT modul_spec_fkey FOREIGN KEY ( spec )
REFERENCES spec ( id_spec ) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
ALTER TABLE modul OWNER TO p o s t g r e s ;
135
136
137
−− Table : other_examiners
138
139
−− DROP TABLE other_examiners ;
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
CREATE TABLE other_examiners
(
modul character varying ( 3 2 ) NOT NULL,
person character varying ( 3 2 ) NOT NULL,
CONSTRAINT other_examiners_modul_fkey FOREIGN KEY ( modul )
REFERENCES modul ( id_modul ) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION ,
CONSTRAINT other_examiners_person_fkey FOREIGN KEY (
person )
REFERENCES person ( i d _ pe r s o n ) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
ALTER TABLE other_examiners OWNER TO p o s t g r e s ;
156
20
157
158
−− Table : person
159
160
−− DROP TABLE person ;
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
CREATE TABLE person
(
i d _ p e rs o n character varying ( 3 2 ) NOT NULL,
" name " character varying ( 6 4 ) NOT NULL,
t i t e l character varying ( 3 2 ) ,
w e b s i t e character varying ( 2 5 6 ) ,
" comment " character varying ( 2 5 6 ) ,
i s _ e x a m i n er boolean DEFAULT f a l s e ,
CONSTRAINT person_pkey PRIMARY KEY ( i d _ p e r s o n )
)
WITH (
OIDS=FALSE
);
ALTER TABLE person OWNER TO p o s t g r e s ;
176
177
178
−− Table : room
179
180
−− DROP TABLE room ;
181
182
183
184
185
186
187
188
189
190
191
192
193
CREATE TABLE room
(
id_room character varying ( 3 2 ) NOT NULL,
c a p a c i t y integer ,
" comment " character varying ( 2 5 6 ) ,
" name " character varying ( 3 2 ) ,
CONSTRAINT room_pkey PRIMARY KEY ( id_room )
)
WITH (
OIDS=FALSE
);
ALTER TABLE room OWNER TO p o s t g r e s ;
21
194
195
196
−− Table : spec
197
198
−− DROP TABLE spec ;
199
200
201
202
203
204
205
206
207
208
209
CREATE TABLE spec
(
id_spec character varying ( 3 2 ) NOT NULL,
" name " character varying ( 3 2 ) ,
CONSTRAINT spec_pkey PRIMARY KEY ( id_spec )
)
WITH (
OIDS=FALSE
);
ALTER TABLE spec OWNER TO p o s t g r e s ;
210
211
212
−− Table : supervised_room
213
214
−− DROP TABLE supervised_room ;
215
216
217
218
219
220
221
222
223
224
225
226
227
228
CREATE TABLE supervised_room
(
id_supervised_room character varying ( 3 2 ) NOT NULL,
room character varying ( 3 2 ) ,
person character varying ( 3 2 ) ,
CONSTRAINT supervised_room_pkey PRIMARY KEY (
id_supervised_room ) ,
CONSTRAINT supervised_room_person_fkey FOREIGN KEY (
person )
REFERENCES person ( i d _ pe r s o n ) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION ,
CONSTRAINT supervised_room_room_fkey FOREIGN KEY ( room )
REFERENCES room ( id_room ) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
22
229
230
231
232
WITH (
OIDS=FALSE
);
ALTER TABLE supervised_room OWNER TO p o s t g r e s ;
Listing 2: DBHandler.py
1
2
3
import md5
import psycopg2
import DBInputHandler
4
5
6
7
8
9
class DBHandler :
dbhost = " 14 9. 20 1.6 3. 90 "
# h o s t o f database
dbname = " pp1_2012 "
#name o f database
dbuser = " p o s t g r e s "
#name o f database user
dbpasswd = " h u n t e r 2 "
#password o f database user
10
11
12
conn = None ;
c u r = None ;
operations
# c o n n e c t i o n t o database
# c u r s o r f o r database
13
14
15
16
def _ _ i n i t _ _ ( s e l f ) :
openDB ( dbname )
17
18
19
def __del__ ( s e l f ) :
closeDB ( )
20
21
22
23
24
25
def getColumnNames ( tablename ) :
# r e t u r n s a l i s t o f names o f t h e columnenames o f t h e
t a b l e ’ tablename ’
c u r . execute ( "SELECT attname FROM p g _ a t t r i b u t e ,
pg_class WHERE pg_class . o i d = a t t r e l i d AND attnum
>0 AND relname=%s " , ( tablename , ) )
columnnames = c u r . f e t c h a l l ( )
r e t u r n columnnames
26
27
def checkLogin ( name , password ) :
23
28
29
30
31
32
#checks l o g i n f o r admin i n t e r f a c e ( n o t r e l a t e d t o
database ! )
i f name == " admin " and password . d i g e s t ( ) == md5 . new
( " admin " ) . d i g e s t ( ) :
r e t u r n None
else :
r e t u r n " Irgendwas i s t s c h i e f g e l a u f e n "
33
34
35
36
37
def openDB ( dbname ) :
# connects t o t h e p o s t g r e s q l database w i t h
parameters above
conn = psycopg2 . connect ( h o s t =dbhost , database=
dbname , user=dbuser , password=dbpasswd )
c u r = conn . c u r s o r ( )
38
39
40
41
def closeDB ( ) :
conn . c l o s e ( )
cur . close ( )
42
43
44
45
46
def g e t L i s t ( tablename ) :
c u r . execute ( " s e l e c t ∗ from %s " , ( tablename , ) )
rows = c u r . f e t c h a l l ( )
columnnames = getColumnNames ( tablename )
47
48
r e t u r n z i p ( columnnames , rows )
Listing 3: DBInputHandler.py
1
2
import md5
import psycopg2
3
4
5
6
def insertRoom ( id_room , name , c a p a c i t y , comment ) :
c u r . execute ( " i n s e r t i n t o room ( id_room , name , c a p a c i t y ,
comment ) v a l u e s ( ’% s ’ , ’% s ’ , ’% s ’ , ’% s ’ ) " , ( id_room , name
, c a p a c i t y , comment ) )
7
8
def i n s e r t P e r s o n ( id_person , name , t i t e l , website , comment ) ;
24
9
c u r . execute ( " i n s e r t i n t o person ( id_person , name , t i t e l ,
website , comment ) v a l u e s ( ’% s ’ , ’% s ’ , ’% s ’ , ’% s ’ , ’% s ’ ) "
, ( id_person , name , t i t e l , website , comment ) )
10
11
12
def insertExamDate ( id_exam_date , i d _ s u b j e c t , i d _ d a t e ,
a t t e n d e es )
c u r . execute ( " i n s e r t i n t o exam_date ( id_exam_date ,
i d _ s u b j e c t , i d _ d a t e , a t te n d e es ) v a l u e s ( ’% s ’ , ’% s ’ , ’% s
’ , ’% s ’ ) " , ( id_exam_date , i d _ s u b j e c t , i d _ d a t e , a t t e n d e e s t
))
13
14
15
def i n s e r t S u b j e c t ( i d _ s u b j e c t , id_course , id_person , name , mode ,
d u r a t i o n , comment )
c u r . execute ( " i n s e r t i n t o s u b j e c t ( i d _ s u b j e c t , id_course ,
id_person , name , mode , d u r a t i o n , comment ) v a l u e s ( ’% s
’ , ’% s ’ , ’% s ’ , ’% s ’ , ’% s ’ , ’% s ’ , ’% s ’ ) " , ( i d _ s u b j e c t ,
id_course , id_person , name , mode , d u r a t i o n , comment )
16
17
18
def i n s e r t C o u r s e ( id_course , degree , name , comment )
c u r . execute ( " i n s e r t i n t o course ( id_course , degree , name ,
comment ) v a l u e s ( ’% s ’ , ’% s ’ , ’% s ’ , ’% s ’ ) " , ( id_course ,
degree , name , comment ) )
19
20
21
def i n s e r t D a t e ( i d _ d a t e , pp , due_date , d e a d l i n e )
c u r . execute ( " i n s e r t i n t o date ( i d _ d a t e , pp , due_date ,
d e a d l i n e ) v a l u e s ( ’% s ’ , ’% s ’ , ’% s ’ , ’% s ’ ) " , ( i d _ d a t e , pp ,
due_date , d e a d l i n e ) )
22
23
24
def i n s e r t E x a m i n e r ( id_second_examiner , i d _ s u b j e c t ,
i d _ p e rs o n )
c u r . execute ( " i n s e r t i n t o second_examiner (
id_second_examiner , i d _ s u b j e c t , i d _ p e rs o n ) v a l u e s
( ’% s ’ , ’% s ’ , ’% s ’ ) " , ( id_second_examiner , i d _ s u b j e c t ,
i d _ p er s o n ) )
25
26
27
def combine ( t a r g e t , i d _ o b j e c t 1 , i d _ o b j e c t 2 )
#target − Zieltabelle
25
28
pass #TODO c u r . execute ( " " , ( ) )
29
30
31
def d e l e t e ( tablename , i d _ o b j e c t )
c u r . execute ( " d e l e t e from %s where %s = %s " , ( tablename ,
" i d _ " +tablename , i d _ o b j e c t )
32
33
34
35
36
37
38
def checkLogin ( name , password ) :
#checks l o g i n f o r admin i n t e r f a c e ( n o t r e l a t e d t o
database ! )
i f name == " admin " and password . d i g e s t ( ) == md5 . new ( "
admin " ) . d i g e s t ( ) :
r e t u r n None
else :
r e t u r n " Irgendwas i s t s c h i e f g e l a u f e n "
39
40
41
42
43
def openDB ( dbname ) :
# connects t o t h e p o s t g r e s q l database w i t h parameters
above
conn = psycopg2 . connect ( h o s t =dbhost , database=dbname ,
user=dbuser , password=dbpasswd
)
c u r = conn . c u r s o r ( )
26
7.3 Literaturverzeichnis
http://wiki.debian.org/ (Stand: Dez. 2011)
http://www.postgresql.org/ (Stand: Dez. 2011)
http://de.wikipedia.org/wiki/Normalisierung_(Datenbank) (Stand: Dez. 2011)
http://www..python.org (Stand: Dez. 2011)
http://initd.org/psycopg/ (Stand: Dez. 2011)
http://www.tinohempel.de/info/info/datenbank/
Peter Kaiser, Johannes Ernesti: Python - Das umfassende Handbuch,Galileo
Computing,Bonn,2008
Alfons Kemper, Andre Eickler: Datenbanksysteme - Eine Einführung, Oldenbourg
Verlag,München,2009
Jürgen Thomas, Einführung in SQL, Wikibooks, 2011
27
Herunterladen