Datenbanken-Praktikum SS2012
Bewertungen
Hr. Krechel kündigt für Blatt 11 an: Das letzte Blatt das am Dienstag ausgegeben wird korrigiere ich als kleine Entschädigung für die späte
Versorgung mit den nötigen Informationen alleine.
Ich melde die Punktzahlen am MO 16.7. an Hr. Krechel. Bitte Einsprüche bis dahin!
Jetzt neu (10.7.) Es gibt jetzt die vollständige Bewertung von Blatt 10 eingetragen. Bitte rechnet wie üblich nochmal nach, und prüft auch,
ob ihr denkt dass euch weitere Bonuspunkte zustehen ;-). Die Korrekturvorgaben findet ihr unten
im Abschnitt "Blatt 10"
Jetzt neu (25.6:) Bewertung von Blatt 7 ist jetzt da. Bitte rechnet meine Punktzahlen nochmal nach! Die Korrekturvorgaben findet ihr unten
im Abschnitt "Blatt 7". Wie ihr dort erkennen könnt, gibt es bis zu 7 Bonuspunkte für Sonderfeatures. Da ich die Bonus-Sachen nicht mehr
im Kopf habe und nicht alle eure Abgaben nochmal durchklicken will: prüft nach, ob ihr zusätzliche Entitäten erfunden hattet, und meldet euch bei mir ;-).
Ich habe allerdings versucht, eher weniger abzuziehen, um das aktuelle Chaos ein wenig auszugleichen.
Update 20.6.:
In den Bewertungen von Blatt 7: habe ich teilweise Kritikpunkte der Form "Keine Beschreibung der Umformungen abgegeben (Normalisierungen, Tabellen für Relationen)."
eingetragen. Zumindest den Teil "Tabellen für Relationen" hatte ich aber nicht in der Liste "abzugebende Dinge" weiter unten stehen.
Deshalb gibt es dafür keine Punktabzüge. Nur Normalisierungen hätten beschrieben werden sollen, aber falls das fehlt werde ich nicht allzuviel abziehen.
Danke für eure Evaluation: SS12-Datenbanken_P-FG.pdf
Blatt 11
Korrektur erfolgt, wie oben angekündigt, durch Hr. Krechel. Also unbedingt über ReadMi abgeben, nicht per Mail an mich.
Falls eine Fragestunde nötig ist, meldet euch per Mail bei mir zwecks Koordinierung eines Termins. Ansonsten könnt ihr mir Fragen per Mail schicken.
Ein paar Hinweise:
- Die Information "schwarz/weiß oder Farbfilm" könntet ihr noch in euer Datenmodell einbauen.
- Wichtig ist, dass ihr eine gute Fehlerbehandlung einbaut: wenn eine Zeile aus der CSV-Datei nicht den Anforderungen genügt (nach einem Split
sind z.B. nicht genau 8 Elemente übrig (die Zahl "8" habe ich jetzt geraten ;-)) ), dann spuckt eine Fehlermeldung aus und brecht am Besten
den Import ab. Es sollte keine "ArrayIndexOutOfBounds" fliegen.
- Auch wenn Pflichtfelder nicht gefüllt sind (z.B. der Titel) sollte euer Import eine sinnvolle Fehlermeldung geben und abbrechen.
Es wäre denkbar, dass Hr. Krechel die Stabilität eures Programms mit fehlerhaften Dateien prüft ;-). Nur doppelte Datensätze (z.B. doppelter Titel bei gleichem Drehjahr)
müssen nicht abgefangen werden, das macht die Datenbank schon eindeutig genug. Falls ihr nicht den Titel als Primärschlüssel verwendet,
sondern eine künstliche ID, dann solltet ihr trotzdem doppelte Datensätze verhindern (entweder programmatisch durch einen Check "Titel/Drehjahr bereits vorhanden"
oder durch einen Unique Index in der Datenbank).
- Nice to have: eine Art Protokollierung: jeder importierte Datensatz wird z.B. auf der Konsole ausgegeben. Hat den Vorteil,
dass man bei einem unbekannten Fehler sieht, wie weit der Import kam.
- Nice to have: wie im Praktikum erklärt, birgt das Parsen von CSV-Dateien viele Fallstricke (Feld-Trennzeichen kommt in einem String vor,
ein String enthält einen Zeilenumbruch). Diese Probleme wurden schon von vielen Leuten gelöst. Eventuell findet ihr eine Library, die
eine CSV-Datei einliest und eine Liste von Zeilen (die Zeileninhalte als Array) ausspuckt. Dann müsst ihr euch eventuell garnicht um
syntaktisch falsche Dateien kümmern. Falls ihr eine solche Library findet, weist Hr. Krechel in einer Readme-Datei darauf hin, dass ihr diese
verwendet, und schreibt auch dazu, dass ich für ein solches Feature Bonuspunkte vergeben hätte ;-).
- Nice to have (Update 9.7.): eine hartcodierte Verbindung zur Datenbank ist relativ unschön, weil man zum Starten des Programms
jedesmal den Quellcode ändern muss.
- Variante 1: Datenbank-URL (Server, Port, Datenbank), Username und Passwort als Kommandozeilenparameter. In der Bedienung eher unpraktisch.
- Variante 2: Programm fragt Verbindungsdaten beim Start vom User ab. Unpraktisch, weil man das jedesmal eintippen mus.
- Variante 3: Einlesen aus Konfigurationsdatei. Beispiel: http://www.exampledepot.com/egs/java.util/Props.html
(das Beispiel hat nur den Nachteil, dass jede gefangene Exception verschluckt wird - das sollte bei euch natürlich nicht passieren).
- Und final zur Abgabe: außer dem Programm benötigt Hr. Krechel zum Testen natürlich das Script, um die Datenbanktabellen zu erzeugen,
und das ER-Diagramm ist bestimmt auch sinnvoll. Gebt also auch diese beiden Teile ab. Falls ihr etwas an den Tabellen ändern müsst (z.B. um
"schwarz/weiß oder Farbfilm" abzubilden), passt SQL-Script und Diagramm an.
Blatt 9
Da wir die letzten drei Statements des Blattes nicht besprochen hatten, hier die Krechelsche Musterlösung:
Wer sitzt im Zimmer mit der größten Zimmernummer?
SELECT D.name FROM dozent D WHERE D.zimmernummer = (SELECT
max(D.zimmernummer) FROM dozent D);
Was sind die Vornamen von Dozenten, die Veranstaltungen halten, die sich in der
Veranstaltungsnummer nur um 1 voneinander unterscheiden?
SELECT DISTINCT D.vorname FROM dozent D, veranstaltung V
WHERE V.dozent=D.kuerzel AND
V.nummer=ANY(SELECT VV.nummer+1 FROM veranstaltung VV
WHERE VV.dozent=D.kuerzel);
Wie viele Dozenten halten Veranstaltungen, deren Nummer kleiner ist als der
Mittelwert der Nummern aller Veranstaltungen?
SELECT count(DISTINCT dozent) FROM veranstaltung V
WHERE V.nummer < (SELECT avg(nummer) FROM veranstaltung);
Blatt 10Blatt 10
Hr. Krechels Korrekturvorgaben als Leitfaden: KorrekturvorgabenBlatt10.pdf
Abweichung zu Read.mi: Blatt 10 kann bis Dienstag 26.6. 23:59 per Mail bei mir abgegeben werden.
Im folgenden ein paar Beispiele für Funktionen. Hoffentlich helfen sie weiter.
Beispiel 1
In diesem Beispiel gibt es:
- Tabelle "Kunde": Name, Geburtsdatum
- Tabelle "Wohnort": Postleitzahl, Ort, Strasse und Hausnummer als eindeutige Kombination.
Da das aber ein sehr aufwändiger Primärschlüssel ist, habe ich eine künstlich generierte ID eingefügt und einen Unique Index über alle anderen Spalten gelegt
- Tabelle "KundeWohnort": verknüpft den Kunden mit seinem Wohnort (ein Kunde hat genau einen Wohnort, an einem Wohnort können n Kunden wohnen).
Da ein Kunde nur einen Wohnort haben kann, ist die Spalte "Kundenname" der Primärschlüssel.
Tabellen erzeugen
create table Kunde
(
Name varchar(100) not null,
Geburtsdatum date,
constraint Kunde_PK primary key (Name)
);
create table Wohnort
(
ID serial not null,
Postleitzahl char(5) not null,
Ort varchar(100) not null,
Strasse varchar(100) not null,
Hausnummer varchar(5) not null,
constraint Wohnort_PK primary key (id)
);
create unique index Wohnort_IX on Wohnort (Postleitzahl, Ort, Strasse, Hausnummer);
create table KundeWohnort
(
Kundenname varchar(100) not null,
WohnortID int not null,
constraint KundeWohnort_PK primary key (Kundenname),
constraint KundeWohnort_FK1 foreign key (Kundenname) references Kunde(Name) on delete cascade,
constraint KundeWohnort_FK2 foreign key (WohnortID) references Wohnort(ID) on delete cascade
);
Besonderheit hierbei: für Primary und Foreign Key habe ich Namen angegeben. Das hat den Vorteil,
dass man die Schlüssel danach explizit löschen kann. Das geht z.B. so:
alter table KundeWohnort drop constraint KundeWohnort_FK2;
Funktion für Erzeugen von Kunde und Wohnort
Diese Funktion legt einen Kunden an. Beim Wohnort wird geprüft, ob es ihn schon gibt. Wenn nicht wird er angelegt.
Anschließend werden Kunde und Wohnort verbunden.
CREATE or replace FUNCTION KundeErzeugen(varchar, date, char, varchar, varchar, varchar)
RETURNS void
AS'
DECLARE
PName ALIAS FOR $1;
PGeburtsdatum ALIAS for $2;
PPostleitzahl ALIAS for $3;
POrt ALIAS for $4;
PStrasse ALIAS for $5;
PHausnummer ALIAS for $6;
zeile Wohnort%ROWTYPE;
BEGIN
SELECT * INTO zeile FROM Wohnort where Postleitzahl = PPostleitzahl and Ort = POrt and Strasse = PStrasse and Hausnummer = PHausnummer;
IF NOT FOUND THEN
insert into Wohnort (Postleitzahl, Ort, Strasse, Hausnummer) values (PPostleitzahl, POrt, PStrasse, PHausnummer);
END IF;
--Kunde erzeugen:
insert into Kunde (Name, Geburtsdatum) values (PName, PGeburtsdatum);
--ID des Wohnorts holen. Der ist entweder vorhanden oder neu.
SELECT * INTO zeile FROM Wohnort where Postleitzahl = PPostleitzahl and Ort = POrt and Strasse = PStrasse and Hausnummer = PHausnummer;
insert into KundeWohnort (Kundenname, WohnortID) values (PName, zeile.ID);
END;
' LANGUAGE 'plpgsql';
Daten anlegen
select KundeErzeugen('Hans Mustermann', to_date('15.06.1960', 'DD.MM.YYYY'), '65321', 'Wiesbaden', 'Biebricher Allee', '12');
select KundeErzeugen('Gaby Mustermann', to_date('29.02.1990', 'DD.MM.YYYY'), '65321', 'Wiesbaden', 'Biebricher Allee', '12');
select * from wohnort;
select * from kunde;
select * from kundeWohnort;
Besonderheit beim Aufruf der Funktion: man beachte die Konvertierung des Datumsparameters: wann immer man in einem SQL-Statement
ein Datum als String angibt, sollte man dieses über die Funktion "to_date" eingeben, denn hier kann man das Format des Datums deklarieren.
Ansonsten bekommt man Probleme, wenn der Server englisches Datumsformat verwendet, im SQL-String aber ein deutsches Datum steht. Im günstigsten Fall
gibt das eine Fehlermeldung ;-).
Dicke Besonderheit: ich habe im zweiten Fall den 29.2. angegeben, den es 1990 wohl nicht gab. PostgreSQL konvertiert das automatisch in den 1.3.1990.
Dafür ein großes "Pfui!" von mir!
Danach aufräumen:
Beim "drop" müssen die Parametertypen angegeben werden!
drop FUNCTION kundeerzeugen(varchar, date, char, varchar, varchar, varchar);
drop table KundeWohnort;
drop table Kunde;
drop table Wohnort;
Beispiel 2: Funktion mit Fehlermeldung
Im folgenden wird die Funktion "KundeLoeschen" angelegt, die prüft, ob der Kunde gerade eine Ausleihe aktiv hat. Wenn dies der Fall
ist, wird eine Fehlermeldung erzeugt. Ansonsten wird er gelöscht.
Tabellen erzeugen
create table Kunde
(
Name varchar(100) not null,
Geburtsdatum date,
constraint Kunde_PK primary key (Name)
);
--Wohnort aus letztem Beispiel ist hier nicht nötig.
create table DVD
(
asin varchar(20) not null,
constraint DVD_PK primary key (asin)
);
--Hier müsste eigentlich die Tabelle der Produktion kommen, die auf der DVD enthalten ist!
create table Exemplar
(
nummer int not null,
asin varchar(20) not null,
constraint Exemplar_PK primary key (nummer, asin),
constraint Exemplar_FK1 foreign key (asin) references DVD (asin) on delete cascade
);
create table Verleihvorgang
(
asin varchar(20) not null,
nummer int not null,
kunde varchar(100) not null,
constraint Verleihvorgang_PK primary key (asin, nummer, kunde),
constraint Verleihvorgang_FK1 foreign key (asin, nummer) references Exemplar(asin, nummer) on delete restrict,
constraint Verleihvorgang_FK2 foreign key (kunde) references KUNDE (name) on delete restrict
);
Daten anlegen
insert into DVD (asin) values ('0815');
insert into DVD (asin) values ('4711');
insert into Exemplar (asin, nummer) values ('0815', 1);
insert into Exemplar (asin, nummer) values ('0815', 2);
insert into Exemplar (asin, nummer) values ('4711', 1);
insert into Exemplar (asin, nummer) values ('4711', 2);
insert into Verleihvorgang (asin, nummer, kunde) values ('0815', 2, 'Hans Mustermann');
Funktion für Löschen des Kunden
CREATE or replace FUNCTION KundeLoeschen(varchar)
RETURNS void
AS '
DECLARE
PName ALIAS FOR $1;
zeileVerleih Verleihvorgang%ROWTYPE;
zeileKunde Kunde%ROWTYPE;
BEGIN
--Gibt es den Kunden überhaupt?
select * INTO zeileKunde from Kunde where Name = PName;
IF NOT FOUND THEN
Raise Exception ''Der Kunde % wurde nicht gefunden!'', PName;
END IF;
SELECT * INTO zeileVerleih FROM Verleihvorgang where Kunde = PName;
IF FOUND THEN
Raise Exception ''Der Kunde % hat mindestens einen Verleihvorgang'', PName;
END IF;
--Kunde löschen
delete from Kunde where Name = PName;
END;
' LANGUAGE 'plpgsql';
Hinweis: Die Zeile mit der Fehlermeldung sieht eigentlich so aus: "Raise Exception 'Der Kunde % hat mindestens einen Verleihvorgang', PName;"
Da ich im Beispiel das Hochkomma ' als große Klammerung um den Funktionsrumpf benutzt habe, muss ich es beim Erzeugen der Fehlermeldung
maskieren durch Verdopplung.
Alternativ findet man in Hr. Krechels Folien und im Web häufig das doppelte "$". Dies klappt allerdings in SquirrelSQL nicht, hier kommt eine Fehlermeldung
"Error: FEHLER: Dollar-Quotes nicht abgeschlossen bei »$$
DECLARE
PName ALIAS FOR $1«"
Über "pgadmin3" klappt das Anlegen einer solchen Function allerdings.
Funktion ausführen
select KundeLoeschen ('Hans Mustermann');
select KundeLoeschen ('Evelyn Nichtvorhanden');
select KundeLoeschen ('Gaby Mustermann');
Aufruf 1 wird einen Fehler bringen "Error: FEHLER: Der Kunde Hans Mustermann hat mindestens einen Verleihvorgang". Aufruf 2:
"Error: FEHLER: Der Kunde Evelyn Nichtvorhanden wurde nicht gefunden!". Aufruf 3 klappt.
Danach aufräumen:
Beim "drop" müssen die Parametertypen angegeben werden!
drop FUNCTION KundeLoeschen(varchar);
drop table Verleihvorgang;
drop table Exemplar;
drop table DVD;
drop table Kunde;
Beispiel 3: Alter des Kunden
Die folgende Funktion ermittelt das Alter des Kunden in Jahren. Sie nutzt die Tabelle "Kunde" aus Beispiel 1.
Funktion erzeugen
CREATE or replace FUNCTION Alter(varchar)
RETURNS Int
AS '
DECLARE
PName ALIAS FOR $1;
zeileKunde Kunde%ROWTYPE;
BEGIN
--Gibt es den Kunden überhaupt?
select * INTO zeileKunde from Kunde where Name = PName;
IF NOT FOUND THEN
Raise Exception ''Der Kunde % wurde nicht gefunden!'', PName;
END IF;
return Extract (year from Age(zeileKunde.Geburtsdatum));
END;
' LANGUAGE 'plpgsql';
In den mir bekannten Datenbanken kann man zwei Datumswerte voneinander abziehen und erhält dann z.B. die Differenz in Tagen oder Sekunden.
Aber in Postgres gibt es eine spezielle Funktion "Age" für die Differenz von übergebenem Datum und "heute". Diese Funktion gibt ein "interval"
zurück, und dessen Komponente "year" wird mittels der Funktion "extract" extrahiert.
Funktion ausführen
select Alter ('Hans Mustermann');
select Alter ('Gaby Mustermann');
select Alter ('Evelyn Invalid');
Danach aufräumen:
drop FUNCTION Alter(varchar);
SquirrelSQL
"SquirrelSQL" ist ein Tool, um Datenbankstatements auszuführen. Dank Nutzung von "JDBC" (Java Database Connectivity) ist dies
für alle Datenbanken möglich, die einen JDBC-Treiber anbieten.
Installation
Homepage: http://www.squirrelsql.org/
Download: http://www.squirrelsql.org/#installation (ich empfehle "Plain zip format of SQuirreL 3.3.0")
Danach wird man die SourceForge-Seite weitergeleitet. Hier "squirrel-sql-3.3.0-standard.tar.gz" herunterladen.
Entpacken und "squirrel-sql.bat" (bzw. "squirrel-sql.sh" unter Linux) ausführen.
JDBC-Treiber einrichten
Zuerst benötigen wir den JDBC-Treiber der PostgreSQL:
http://jdbc.postgresql.org/download.html
Hier nehmen wir die "Current version: JDBC4 Postgresql Driver, Version 9.1-902" (zu prüfen wäre, ob man für die Nutzung der FH-Datenbank
einen JDBC-Treiber benötigt, der kompatibel zu Version 8.4 ist)
Die JAR-Datei "postgresql-9.1-902.jdbc4.jar" legt man irgendwohin.
Jetzt gehen wir in SquirrelSQL auf den Karteireiter "Driver", suchen "PostgreSQL" in der Liste und wählen im Kontextmenü "Modify Driver":
In dem sich öffenen Dialog gehen wir auf den Karteireiter "Extra Class Path" und fügen über "Add" die eben heruntergeladene JAR-Datei zu.
Datenbankverbindung einrichten
Auf dem Karteireiter "Aliases" wählen wir im Kontextmenü "New Alias..." aus:
Man wählt den Driver "PostgreSQL" (hat jetzt einen blauen Haken, da wir ihn im vorherigen Schritt eingerichtet hatten).
Im Feld "URL" steht der PostgreSQL-Connectionstring mit einigen Platzhaltern in spitzen Klammern. Diese sind zum Teil optional (z.B. der Port).
Den Host und den Zieldatenbanknamen muss man aber auf jeden Fall angeben.
Mehr Details zum Format für eine PostgreSQL-URL:
http://jdbc.postgresql.org/documentation/91/connect.html
Benutzername und Passwort sind an dieser Stelle optional, man könnte sie auch bei jedem Verbindungsversuch neu eingeben.
Über den Button "Test" kann man die Verbindung mit den eingegebenen Parametern testen lassen
Will man die Verbindung später aufbauen, wählt man im Kontextmenü "Connect":
SquirrelSQL verwenden
Jetzt erscheint ein Fenster, in dem man auf dem Karteireiter "SQL" ein großes Eingabefeld findet (muss man sich eventuell erst groß ziehen,
ist in der Standardeinstellung ziemlich klein).
Einen eingegebenen SQL-Befehl kann man mittels des Toolbar-Buttons "Run SQL" (im Screenshot markiert) ausführen.
Anmerkung: es wird nur das Statement ausgeführt, auf dem der Cursor gerade steht. Man kann allerdings auch mehrere Befehle markieren
und dadurch im Block ausführen lassen.
Blatt 8
Aufgabe 1: "Operatorbaum":
Wie im Praktikum kurz gezeigt, hier ein Beispiel für einen Operatorbaum:
http://wikis.gm.fh-koeln.de/wiki_db/Datenbanken/Operatorbaum
(Die Symbole für die Operationen entsprechen nicht komplett Hr. Krechels Vorgaben, vermutlich weil der Zeichensatz der Webseite
nicht alle Zeichen enthält und deshalb Ersatz gesucht wurde)
Achtung: Im Praktikum heute habe ich den Baum falsch herum aufgebaut ;-).
Postgres-JOIN-Syntax:
http://www.postgresql.org/docs/9.0/static/queries-table-expressions.html
Aufgabe 2: Erklärung der "Teilmenge von"/"Menge von"-Symbole: http://de.wikipedia.org/wiki/Teilmenge
"A ist eine Teilmenge von B ..., wenn jedes Element von A auch in B enthalten ist. Wenn B zudem weitere Elemente enthält, die nicht in A
enthalten sind, so ist A eine echte Teilmenge."
Blatt 7
Hr. Krechels Korrekturvorgaben als Leitfaden: KorrekturvorgabenBlatt7.pdf
Hinweise zur Abgabe:
- Falls ihr bei Blatt 3 Punktabzüge bekommen habt, dann korrigiert bitte das ER-Modell, bevor ihr mit der Bearbeitung von Blatt 7 beginnt.
- Zur Abgabe gehört auch das aktuelle ER-Modell (egal ob Änderungen zur ersten Abgabe nötig waren oder nicht)
- Falls ihr Umformungen vornehmt (Normalisierung, "Behebung" von Mehrfachattributen), dann legt eine Beschreibung der Änderungen bei.
Blatt 5
Beispiellösung aus Studentenkreisen: Blatt5.pdf
Ein paar Infos zu Aufgabe 1:
Für die Lösung gibt es wohl drei Möglichkeiten:
Variante 1: Hülle der Funktionalen Abhängigkeiten
Die Hülle (closure) kann z.B. durch Anwendung der Inferenzregeln berechnet werden. Sind die Hüllen F+ von F und G+ von G identisch, sind die
Abhängigkeitsmengen äquivalent.
Siehe z.B. für diese Theorie: http://books.google.de/books?id=REgdRECfcRIC&pg=PA316&lpg=PA316#v=onepage&q&f=false
Und ein konkretes Beispiel für die Hülle: http://web.fhnw.ch/plattformen/edbs/modulunterlagen/RelDesign.pdf
(auf Seite 12), leider unkommentiert
Hier ein Beispiel, das die Schritte aufzeigt: http://cis.csuohio.edu/~matos/notes/cis-611/ClassNotes/17-FuncDepend.html
Aus der Aufgabenstellung habe ich mir diese Schritte zusammengereimt, um zu beweisen, dass "AC -> D" aus F in der Hülle von G enthalten ist:
- Mittels Dekompositions-/Zerlegungsregel (in Vorlesungs-PDF: Projektion) wird A -> CD zerlegt in A -> C und A -> D
- Erweiterung (in Vorlesungs-PDF: Augmentation) von A -> D um C ergibt AC -> CD
- AC -> CD durch Dekomposition zerlegen in AC -> D und AC -> C: hier haben wir die Abhängigkeit aus F in der Hülle gefunden
Variante 2: über die Attributhülle
Attributhülle (attribute closure): http://de.wikipedia.org/wiki/Funktionale_Abh%C3%A4ngigkeit#Attributh.C3.BClle
Zitat: "Die Attributehülle (alpha plus) eines bestimmten Attributs ist eine Liste aller Attribute, die von (alpha) funktional abhängen.
Im kleinsten Fall ist die Attributhülle nur das Attribut selbst, da keine anderen Attribute von ihm abhängen."
Das Verfahren ist hier erläutert: http://www.scribd.com/doc/3275832/Funktionale-Abhangigkeit (eigentlich
eine Nervseite...), Seite 12
Das Vorgehen ist eigentlich nur aus der Formel erkennbar: für jede funktionale Abhängig in F wird die Attributhülle der linken Seite in G
ermittelt und geprüft, ob die rechte Seite der funktionalen Abhängigkeit in dieser Attributhülle enthalten ist.
Beispiel für "{A, C} -> {D}" von "F": Attributhülle von "{A, C}" in "G" ist "{A, C, D}". Diese enthält die rechte Seite der Abhängigkeit, nämlich "{D}"
Gilt dies für alle funktionalen Abhängigkeiten von F und G, dann sind die Abhängigkeitsmengen äquivalent.
Dieser Algorithmus ist auch als "RAP-Algorithmus" zu finden, siehe http://www.informatik.tu-cottbus.de/~tk/lehre/DBING_SS04/7_Normalisierung_und_FD.pdf,
Seite 15-17 (hier wird nicht die Äquivalenz betrachtet, sondern nur die Frage "ist eine Abhängigkeit X -> Y in der Hülle F+ enthalten?".
Variante 3: über die kanonische Überdeckung
Siehe Aufgabe 2
Hier zusammengefaßt die Zwischenergebnisse:
Schritt 1: Linksreduktion von F ergibt:
- A->C
- A->D (hier kann man "C" reduzieren, weil Attributhülle von A = {A,C,D} - enthält die rechte Seite immer noch)
- E->AD
- E->H
Schritt 2: Rechtsreduktion von F ergibt:
- A->C
- A->D
- E->A (hier kann man D reduzieren, weil Attributhülle von E auf Basis der FD-Menge {A->C, A->D, E->A, E->H} = {A,C,D,E,H}
- E->H
Schritt 3: Funktionale Abhängigkeiten mit leerer rechter Seite rauswerfen: haben wir nicht.
Schritt 4: zusammenfassen:
Und hiermit haben wir schon die Menge G erhalten! D.h. G ist die kanonische Überdeckung von F.
Umgekehrt müsste man auch von G die kanonische Überdeckung ermitteln, aber hier ist nichts reduzierbar (wie auch, wenn es schon eine kanonische
Überdeckung ist ;-)?).
Infos zu Aufgabe 2:
"kanonische Überdeckung": http://de.wikipedia.org/wiki/Kanonische_%C3%9Cberdeckung
Dort finden sich Verweise auf die Algorithmen zur Ermittlung der Kanonischen Überdeckung.
Und zur Erklärung der ganzen Symbole ;-): http://de.wikipedia.org/wiki/Griechisches_Alphabet
und http://de.wikipedia.org/wiki/Mathematische_Symbole#Mengenlehre
Definition der kanonischen Überdeckung in "Datenbanksysteme" (Kemper, Eickler):
http://books.google.de/books?id=xpNefMq5nYwC&pg=PA177&lpg=PA177 (Seite 177
ist relevant)
Ein Vorlesungs-PDF, das die Theorien anders formuliert darstellt:
http://greententacle.techfak.uni-bielefeld.de/lehrverwaltung/upload/attachment/1-DB_08_FDs.pdf
Und noch ein PDF, das z.B. die Links-/Rechtsreduktion übersichtlicher darstellt:
http://www.cs.uni-paderborn.de/fileadmin/Informatik/AG-Engels/Lehre/SS09/DaBa/Folien/Kapitel_3_-_Relationaler_Entwurf_-_Teil_5.pdf
Kurzbeschreibung Linksreduktion:
Für jede funktionale Abhängigkeit, die auf der linken Seite mehr als ein Attribut hat, wird geprüft, ob man eines der Attribute
reduzieren kann. Dazu ermittelt man die Attributhülle des Rests der linken Seite. Bei "ABC -> D" würde man also erst prüfen, ob man A streichen kann:
Attributhülle von "BC" ermitteln. Als Ausgangslage enthält die Attributhüller hier "BC", und dann berechnet man sie nach den bekannten Schritten.
Dabei betrachtet man auch die originale funktionale Abhängigkeit "ABC -> D", nicht die reduzierte Variante. Hat man also bereits A, B und C
in der Attributhülle, dann kommt durch "ABC -> D" auch D ins Spiel.
Enthält die Attributhülle von "BC" die komplette rechte Seite (hier: "D"), kann man "A" streichen.
Falls man mehr als zwei Attribute auf der linken Seite hat, kann man eventuell mehr als ein Attribut reduzieren. Ich denke es macht Sinn,
nach einer erfolgreichen ersten Reduzieren den zweiten Reduzierungslauf mit einer Menge an funktionalen Abhängigkeiten zu starten, die statt der
Originalabhängigkeit mit z.B. drei Attributen auf der linken Seite bereits die reduzierte Abhängigkeit enthält.
Kurzbeschreibung Rechtsreduktion:
Für jede funktionale Abhängigkeit (auch solche, die auf der rechten Seite nur ein Attribut hat - bei erfolgreicher Reduktion erhält
man hier eine leere Menge), wird geprüft, ob man eines der Attribute reduzieren kann. Dazu ermittelt man die Attributhülle der
linken Seite, allerdings aus einer Menge von funktionalen Abhängigkeiten, in der aus der aktuellen Abhängigkeit das gerade betrachtete
Attribut der rechten Seite entfernt wurde! Bei "AB -> CD" würde man also erst prüfen, ob man C streichen kann:
Attributhülle von "AB" ermitteln. Dabei ersetzt man die originale funktionale Abhängigkeit "AB -> CD" durch die reduzierte Form
"AB -> D" (also das, was nach der Reduktion übrig bleibt).
Enthält die Attributhülle von "AB" immer noch den Rest der rechten Seite (als "D"), kann man "C" aus der rechten Seite streichen.
Auch hier prüft man reduzierte funktionalen Abhängigkeit weiter, d.h. im nächsten Schritt würde man prüfen, ob aus
"AB -> D" auch das "D" reduzierbar ist.
Blatt 4
Hier eine beispielhafte Lösung eines Studenten: Blatt4.odt
Blatt 3
Bewertung siehe oben.
Hr. Krechels Korrekturvorgaben als Leitfaden: KorrekturvorgabenBlatt3.pdf
Allerdings habe ich daran Änderungen, z.B. möchte ich das Genre als eigene Entität sehen.
Bitte Rückmeldung, wenn euch etwas falsch vorkommt oder ich euch ungerecht behandelt habe!
Ein Hinweis von Hr. Krechel dazu:
Da die zweite Abgabe auf der Ersten aufbauen wird, ist es natürlich wichtig, dass den Studenten ihre Fehler erklärt werden
und diese ihre ER-Modelle verbessern.
Also nutzt die Zeit bis zur zweiten Abgabe, um euer Modell zu überarbeiten.
In Read.mi stand der Kommentar "Zum Bestehen muss etwas abgegeben werden". Dazu hat Hr. Krechel per Mail etwas gesagt:
Obligatorisch im read.mi bedeutet, dass etwas hochgeladen wird. Man muss jedoch nicht 50% der Punkte erreichen. Da die Abgaben
inhaltlich aufeinander aufbauen werden ist es natürlich schwer ein normalisiertes relationales Modell ohne das entsprechende ER-Modell
zu erstellen...
Da ich niemanden wegen mehrdeutiger Ansage durch mich jetzt schon aus dem Praktikum rauswerfen will, werde ich Morgen in der Vorlesung
festlegen das nur 3 der 4 Abgaben abgegeben werden müssen. Trotzdem muss man bei dreien mindestens 50% der Punkte erreichen ...
Und zur Frage "Werden zum Bestehen die Hälfte von 27 oder die Hälfte von 30 Punkte benötigt?" hat Hr. Krechel geantwortet:
Ich kann mit einem bestanden mit 13,5 Punkten leben auch wenn in der Aufgabenstellung "Die folgenden Angaben beschreiben nur eine
Teil der Informationen die diese Datenbank liefern soll. Es ist ihre Aufgabe alle benötigten Information und Funktionen für diese Anwendung
zu erarbeiten" stand.
Ein paar Erkenntnisse aus der Fragestunde:
Kardinalitäten in ternären Relationen:
Ein Beispiel ist hier (am Ende der Seite) zu finden: http://bkb-netz1.dynalias.org/grantz/db/entwurf/3eerm/mehrfach.html
Darstellung einer Generalisierung/Spezialisierung:
Siehe http://www.uni-weimar.de/medien/webis/teaching/lecturenotes/databases/unit-de-conceptual-design2.pdf
(Seite 89 kapitelübergreifend bzw. innerhalb dieser Datei Seite 33 und folgende): scheinbar ist diese Verbindung zwischen Entitäten nicht Teil des
originalen ERM-Diagramms, deshalb gibt es unterschiedliche "Ergänzungsvarianten", darunter auch z.B. ein Sechseck mit dem Inhalt "is-a" (Seite 93/26).
Ich bevorzuge die von Hr. Krechel in der Vorlesung verwendete Variante mit der Relation-Raute und dem Inhalt "Ist". Wichtig dabei: in Richtung der
Parent-Entität muss eine Pfeilspitze stehen.
Abgabe über "read.mi..."
Mittlerweile ist unter https://read.mi.hs-rm.de die Möglichkeit der Abgabe freigeschaltet. Wer das nicht findet
(oder nicht will ;-)), kann auch per Mail an mich abgeben.
Blatt 2
Hier die im Praktikum gezeigte Lösung von Blatt 2, Aufgabe 2:
Aus der ersten Gruppe (ab 16 Uhr): Blatt2_Aufgabe2.dia
Bitte beachten: die Verbindung von "Ticket" zu "Flug" ist vermutlich wirklich unnötig.
Aus der zweiten Gruppe (ab 17:45 Uhr): u2ticket.dia
Lokale PostgreSQL unter Windows
Installation
Download: http://www.postgresql.org/download/windows/ bzw.
direkt hier: http://www.enterprisedb.com/products-services-training/pgdownload
Ich empfehle die neueste Version (aktuell: 9.1.3).
Es gibt auch einen Download ohne Installer (Zip-Datei), aber hier muss man sich alles selbst einrichten - nicht für den Einstieg geeignet.
Bei der Installation klickt man im Prinzip immer nur auf "Weiter". Einzige Besonderheit: während der Installation wird man aufgefordert, das Passwort
für den Benutzer "postgres" (Datenbankbenutzer und gleichzeitig ein lokaler Windows-Benutzer) einzugeben.
Dies muss man sich merken, sonst kann man sich später nicht mit der Datenbank verbinden.
Verbinden mit der Datenbank:
Da "psql.exe" den Login des Windows-Benutzers als Default-Datenbankbenutzernamen verwendet (und der mit hoher Wahrscheinlichkeit nicht "postgres" heißt ;-)),
muss man den Benutzernamen im Aufruf mitgeben. Hier ein Beispiel für die Verwendung des Standardbenutzers "postgres":
psql.exe --username postgres
Tuning:
Die Datenbank wird jetzt automatisch als Hintergrunddienst gestartet. Ich empfehle, den Datenbank-Dienst auf den Starttyp "Manuell" zu stellen und ihn
nur bei Bedarf zu starten und danach wieder zu stoppen.
Administration
Die lokale Datenbank wird mittels "pgAdmin" verwaltet, zu finden im Startmenü hier:
Hier meldet man sich am lokalen Server an und sieht danach dieses Fenster vor sich:
Wer will, kann sich hier eine eigene Datenbank und einen neuen Benutzer anlegen (unter anderem, um eine Spielwiese zu haben, oder auch um z.B. andere
Zeichensätze zu testen).
Schritt 1: neuer Benutzer:
Auf dem ersten Karteireiter einen Loginnamen (heißt hier: "Rollename") vergeben und auf dem zweiten Karteireiter "Definition" ein Passwort eintragen:
Das reicht bereits an Eingaben für einen neuen Benutzer.
Schritt 2: neue Datenbank:
Hier geben wir einen Namen ein und wählen als "Eigentümer" den eben erstellten Benutzer:
Verwenden der Datenbank:
Hier muss der Datenbankname in den "psql"-Argumenten angegeben werden.
psql.exe --dbname=knauftest --username=knauftest
PostgreSQL-Client unter Windows
"psql.exe" ist ein wenig störrisch unter Windows im Bezug auf Umlaute.
Das zeigt sich schon beim Start, wo solch eine Meldung ausgegeben wird:
Warning: Console code page (850) differs from Windows code page (1252)
8-bit characters might not work correctly. See psql reference
page "Notes for Windows users" for details.
"cmd.exe" konfigurieren
Hauptursache ist, dass die Windows-Kommandozeile einen uralten Zeichensatz (eben die CodePage "850",
http://de.wikipedia.org/wiki/Codepage_850 ) verwendet.
Welche Codepage gerade aktuell ist, sieht man über den Befehl "chcp" ohne Parameter:
C:\...\...>chcp
Aktive Codepage: 850.
Schritt 1 ist deshalb, dies umzustellen auf Codepage "1252" (Default-Zeichensatz für alle westeuropäischen Windowsversionen)
C:\...\...>chcp 1252
Aktive Codepage: 1252.
Das macht die Lage aber erstmal eher schlimmer - jetzt werden sogar eingegebene Umlaute falsch dargestellt ;-).
Deshalb in Schritt die Konsolen-Schriftart umstellen: Im Fenstermenü (Fenstericon anklicken) auf "Eigenschaften" gehen:
Die Schriftart von "Rasterschriftart" auf "Lucida Console" ändern:
Jetzt stimmen die eingebenen Umlaute wieder, und auch "psql.exe"-Ausgaben sind korrekt.
Falls man sich versehentlich das gesamte System umgestellt hat (auf meinem Privatrechner kam eine Abfrage "nur für aktuelles Fenster oder für alle?",
unter Windows 7 an der FH scheint die Schriftartänderung aber für alle Konsolen zu gelten), hier ein Screenshot der Originaldaten ("Rasterschriftart"
der Größe "8x12"):
Encoding in "psql.exe"
Auf meinem Privatrechner mit PostgreSQL 9.1 als lokalem Server konnte ich ab hier frei arbeiten. An der FH (Clientversion 8.3) kam allerdings bei Verwendung
von Umlauten z.B. in SELECT-Statements folgenden Fehlermeldung:
FEHLER: ungültige Byte-Sequenz für Kodierung »UTF8«: 0xf6e4fc27
Ursache ist hier, dass "psql.exe" nicht das gleiche Encoding wie die Kommandozeile verwendet. Um das aktuelle Encoding herauszufinden:
datenbankname=> show client_encoding;
client_encoding
-----------------
UTF8
(1 Zeile)
Dies ist eine Beispielausgabe von der FH. Wenn hier "WIN1252" steht, sollten Umlaute klappen. UTF8 allerdings sollte umgestellt werden:
datenbankname=> SET client_encoding TO 'win1252';
SET
Jetzt können wir ein Beispielselect abfeuern:
Kommen die Umlaute hier korrekt heraus, scheint alles OK zu sein.
Nur zur Info: in welchem Encoding die Datenbank auf dem Server liegt, findet man so heraus (Spalte "Kodierung" des Ergebnisses):
knauftest=> \l
Liste der Datenbanken
Name | Eigentümer | Kodierung | Sortierfolge | Zeichentyp
| Zugriffsprivilegien
-----------+------------+-----------+---------------------+---------------------
+-----------------------
knauftest | knauf | UTF8 | German_Germany.1252 | German_Germany.1252
|
postgres | postgres | UTF8 | German_Germany.1252 | German_Germany.1252
|
template0 | postgres | UTF8 | German_Germany.1252 | German_Germany.1252
| =c/postgres +
| | | |
| postgres=CTc/postgres
template1 | postgres | UTF8 | German_Germany.1252 | German_Germany.1252
| =c/postgres +
| | | |
| postgres=CTc/postgres
(4 Zeilen)
Weitere Informationen: http://www.commandprompt.com/ppbook/c12119
"Echtes" UTF8 auf Clientseite
Theoretisch sollte es reichen, die Codepage der Kommandozeile auf "65001" zu setzen
( http://msdn.microsoft.com/en-us/library/windows/desktop/dd317756%28v=vs.85%29.aspx ).
Aber danach rastet psql.exe total aus und spuckt bei fast jeder Aktion Fehler "Nicht genügend Arbeitsspeicher." aus. Das Web liefert leider
viele Treffer zu dieser Meldung, aber keine Lösung.
Encoding in Scripten
Sollen mit dem psql-Befehl "\i" Scripte aus Dateien ausgeführt werden, wird zum Einlesen dieser Datei wohl aktuell in psql eingestellte Kodierung
verwendet. Das kann Probleme bei Sonderzeichen machen. Deshalb sollte man beim Generieren der Scriptdatei darauf achten, unter welchem Encoding man
sie speichert. Der Windows-Editor (Notepad) zum Beispiel bietet im "Speichern unter"-Dialog eine Combobox mit einigen verfügbaren Encodings an. Unter
Windows Vista/Windows 7 ist hier per Default "UTF-8" gewählt. Hat man die Konsole (wie oben beschrieben) auf "Windows1252" umgestellt, sollte man die Scripte
als "ANSI" speichern:
Als "schlechtes" Beispiel sei hier die Scriptdatei von Hr. Krechel ("u1a3.sql") genannt, die im UTF8-Format vorliegt ;-). Hier geht zum Glück nichts kaputt,
aber einige Kommentare enthalten Umlaute und werden falsch ausgegeben.
Stand 10.07.2012
Historie:
21.03.2012: Erstellt
25.03.2012: Postgres-Installation
04.04.2012: Beispiellösung Blatt 2, Aufgabe 2
10.04.2012: Fragestunde 11.4.
11.04.2012: Infos zu Blatt 3
24.04.2012: Bewertung Blatt 3
25.04.2012: Beispiellösung Blatt 4, Links zu Blatt 5
25.04.2012: Ergänzung zur Bewertung
01.05.2012: Weiterer Link zur kanonischen Überdeckung
04.05.2012: Blatt 5 Aufgabe 1: Äquivalenz überarbeitet
10.05.2012: Blatt 5 Aufgabe 1: Lösung für kanonische Überdeckung, mehr Anleitung zur kanonischen Überdeckung
16.05.2012: Hinweise zu Blatt 7
19.05.2012: Beispiellösung Blatt 5
20.05.2012: Beispiellösung Blatt 5 aktualisiert
23.05.2012: Blatt 5: Link zu Mengenlehre-Symbolen verbessert, Infos zu Blatt 8
30.05.2012: Blatt 8: Link zu Operatorbaum und zu Postgres-JOIN-Syntax
11.06.2012: Evaluationsergebnis, SquirrelSQL
16.06.2012: Bewertung Blatt 7: meine Kritikpunkte
20.06.2012: Update Bewertungskriterien Blatt 7, Hinweise Blatt 10
21.06.2012: Funktions-Beispiele für Blatt 10
25.06.2012: Bewertung Blatt 7
28.06.2012: Hinweise Blatt 11, Musterlösung des Rests von Blatt 9, Korrektur einer falschen Matrikelnummer in Bewertung.
04.07.2012: Kritikpunkte zu Blatt 10
09.07.2012: weiteres "nice to have" zu Blatt 11
10.07.2012: Bewertung Blatt 10