Universität Augsburg, Institut für Informatik Dr. W.-T. Balke M. Endres, A. Huhn, T. Preisinger WS 2006/2007 27. Nov. 2006 Lösungsblatt 5 Datenbanksysteme I Aufgabe 1: Projektion πA1 ,...,An (πB1 ,...,Bm (R)) = πA1 ,...,An (R) gilt, wenn {Ai | i = 1, ..., n} ⊆ {Bi | i = 1, ..., m}, d.h. wenn die Attribute Ai in der Menge der Attribute Bi enthalten sind. Aufgabe 2: Division in relationaler Algebra Zu beweisen: R ÷ S = πA (R) \ πA ((πA (R) × S) \ R) Beweis: Wir beweisen die Aussage wie in der Mengenlehre und veranschaulichen die Äquivalenzen anhand folgendem Beispiel: R Programmierer Müller Müller Müller Huber Huber Sprache Java Basic C++ C++ Java S und 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 R ÷ S = πA (R) \ πA ((πA (R) × S) \ R) und den Zuweisungen • R→R×S • A→R • S→S ergibt sich (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 insert insert insert insert insert insert insert insert into into into into into into into into into Produkte Produkte Produkte Produkte Produkte Produkte Produkte Produkte Produkte Values Values Values Values Values Values Values Values Values (1, (2, (3, (4, (5, (6, (7, (8, (9, 1, 1); 1, 2); 2, 1); 2, 2); NULL, 1); NULL, 2); 1, NULL); 2, NULL); 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 1 2 NULL A SUM 3 3 3 A COUNT 3 3 3 A D COUNT 2 2 2 A MAX 2 2 2 A MIN 1 1 1 A AVG 1,5 1,5 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 7 2 1 7 2 1 8 4 3 8 4 3 7 2 1 8 4 3 ANZAHL LAGER 1 1 1 1 1 1 2 2 2 2 2 2 1 1 1 2 2 2 ANZAHL SHOPS NULL 2 1 NULL 2 1 NULL 2 1 NULL 2 1 NULL 2 1 NULL 2 1 ID 1 1 1 2 2 2 3 3 3 4 4 4 7 7 7 8 8 8 ANZAHL LAGER ANZAHL SHOPS 1 1 1 1 1 1 1 2 1 2 1 2 2 1 2 1 2 1 2 2 2 2 2 2 1 NULL 1 NULL 1 NULL 2 NULL 2 NULL 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 INSERT INSERT INSERT INSERT INTO INTO INTO INTO INTO R R R R R VALUES(1,1,’programmer’); VALUES(1,2,’clerk’); VALUES(2,3,’clerk’); VALUES(2,4,’clerk’); 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 FROM GROUP HAVING dept# R BY dept# 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 MINUS (SELECT WHERE GROUP HAVING dept_nr FROM R) dept_nr FROM R job = ’clerk’ BY dept_nr COUNT(*) > 2); 5