Universität Augsburg, Institut für Informatik WS 2006/2007 Dr. W.-T. Balke 27. Nov. 2006 M. Endres, A. Huhn, T. Preisinger Lösungsblatt 5 Aufgabe 1: Projektion Datenbanksysteme I π A1,...,A n (π B1,...,B m (R)) = π A1,...,A n (R) gilt, wenn {A i i = 1,..., n} {B i i = 1,..., m}, d.h. wenn die Attribute A i in der Menge der Attribute B i enthalten sind. Aufgabe 2: Division in relationaler Algebra Zu beweisen: Beweis: R S = π A (R) \ π A ((π A (R) S) \ R) Wir beweisen die Aussage wie in der Mengenlehre und veranschaulichen die Äquivalenzen anhand folgendem Beispiel: R Programmierer Sprache Müller Java Müller Basic Müller C++ Huber C++ Huber Java und S Sprache Basic C++ Java Frage: Welche Programmierer programmieren in allen Sprachen R S Programmierer Müller : Sei r R S 1. s S : [rs] R 2. r π A (R) Wegen 2. genügt es zu zeigen, dass r / π A ((π A (R) S) \ R) x S : [rx] / (π A (R) S) \ R x S : [rx] / π A (R) S [rx] R Die erste Bedingung [rx] / π A (R) S wird immer zu false ausgewertet, die zweite zu true für alle x S, d.h. die Bedingung ist wahr. : Sei r π A (R) \ π A ((π A (R) S) \ R) 1. r / π A ((π A (R) S) \ R) 2. r π A (R) Zu zeigen: s S : [rs] R aus 1. folgt: x S : [rx] / π A (R) S [rx] R. Restliche Argumentation wie bei. 1
Der Divisionoperator erhöht die Ausdruckskraft der Relationenalgebra nicht, sondern wurde nur zur Vereinfachung eingeführt. Aufgabe 3: Relationale Algebra Unter Verwendung von und den Zuweisungen R R S A R S S ergibt sich R S = π A (R) \ π A ((π A (R) S) \ R) (R S) S = π R (R S) \ π R ((π R (R S) S) \ (R S)) R \ π R ((R S) \ (R S)) R \ π R ( ) R \ R Aufgabe 4: Aggregation und Gruppierung in SQL Erstellen der Relation Produkte: CREATE TABLE Produkte ( ID INTEGER Primary Key, Anzahl_Lager INTEGER, Anzahl_Shops INTEGER); insert into Produkte Values (1, 1, 1); insert into Produkte Values (2, 1, 2); insert into Produkte Values (3, 2, 1); insert into Produkte Values (4, 2, 2); insert into Produkte Values (5, NULL, 1); insert into Produkte Values (6, NULL, 2); insert into Produkte Values (7, 1, NULL); insert into Produkte Values (8, 2, NULL); insert into Produkte Values (9, NULL, NULL); 2
a) Anfrage: SELECT Anzahl_Lager, SUM(Anzahl_Shops) as a_sum, COUNT(*) as a_count, COUNT(DISTINCT Anzahl_Shops) as a_d_count, MAX(Anzahl_Shops) as a_max, MIN(Anzahl_Shops) as a_min, AVG(Anzahl_Shops) as a_avg FROM Produkte GROUP BY Anzahl_Lager; ANZAHL LAGER A SUM A COUNT A D COUNT A MAX A MIN A AVG 1 3 3 2 2 1 1,5 2 3 3 2 2 1 1,5 NULL 3 3 2 2 1 1,5 Im Ergebnis kommt nur eine Gruppe für NULL-Werte vor. Bei Gruppierung werden NULLs also als gleich betrachtet. Die Summe ist die Summe der tatsächlichen Werte für Anzahl Shops. NULL hat hier keine Auswirkungen. COUNT(*) zählt alle Tupel, auch das mit Anzahl Shops = NULL. Im Gegensatz dazu zählt COUNT(DISTINCT Anzahl Shops) Einträge mit Anzahl Shops = NULL nicht. MAX(Anzahl Shops) und MIN(Anzahl Shops) liefern das Maximum bzw. das Minimum ohne NULLs zu berücksichtigen, obwohl NULL beim Sortieren von Oracle als größter Wert angesehen wird. AVG(Anzahl Shops) liefert den Duchschnitt der Tupelwerte ungleich NULL. NULL-Werte beeinflussen den Durchschnitt also nicht. b) Anfrage: SELECT * FROM Produkte P1, Produkte P2 WHERE P1.Anzahl_Lager = P2.Anzahl_Lager; Ergebnis: 3
ID ANZAHL LAGER ANZAHL SHOPS ID ANZAHL LAGER ANZAHL SHOPS 7 1 NULL 1 1 1 2 1 2 1 1 1 1 1 1 1 1 1 7 1 NULL 2 1 2 2 1 2 2 1 2 1 1 1 2 1 2 8 2 NULL 3 2 1 4 2 2 3 2 1 3 2 1 3 2 1 8 2 NULL 4 2 2 4 2 2 4 2 2 3 2 1 4 2 2 7 1 NULL 7 1 NULL 2 1 2 7 1 NULL 1 1 1 7 1 NULL 8 2 NULL 8 2 NULL 4 2 2 8 2 NULL 3 2 1 8 2 NULL Tupel mit P1.Anzahl Lager = NULL bzw. P2.Anzahl Lager = NULL fehlen. Das bedeutet NULLs werden hier als unterschiedliche Werte betrachtet (NULL <> NULL). Aufgabe 5: Count-Bug a) Relation erstellen: CREATE TABLE R ( dept_nr INTEGER, emp_nr INTEGER, job varchar(50), PRIMARY KEY (dept_nr, emp_nr)); b) Tupel einfügen: INSERT INTO R VALUES(1,1, programmer ); INSERT INTO R VALUES(1,2, clerk ); INSERT INTO R VALUES(2,3, clerk ); INSERT INTO R VALUES(2,4, clerk ); INSERT INTO R VALUES(3,5, sales ); c) Der erste intuitive Ansatz SELECT dept_nr FROM emp WHERE job = clerk GROUP BY dept_nr HAVING COUNT(*) <= 2 schlägt fehl. In der WHERE-Klausel werden alle Einträge außer denen mit job = clerk eliminiert. Dadurch fallen sämtliche Einträge zum Department 3 weg. Das Department 3 fehlt also auch im Ergebnis, obwohl es rein intuitiv auch gesucht war. 4
Ein anderer Lösungsansatz SELECT dept# FROM R GROUP BY dept# HAVING COUNT(*) <= 2 AND job = clerk ; ist syntaktisch falsch, da Nicht-Gruppierungsattribute wie job nur in Form von Aggregationsausdrücken in einer HAVING-Klausel vorkommen dürfen. Korrekte SQL-Anfrage: (SELECT dept_nr FROM R) MINUS (SELECT dept_nr FROM R WHERE job = clerk GROUP BY dept_nr HAVING COUNT(*) > 2); 5