Übungsblatt 5 - Lösung - Informatik

Werbung
Universität Augsburg, Institut für Informatik
Prof. Dr. W. Kießling
Dr. A. Huhn, F. Wenzel, M. Endres
WS 2009/2010
27. Nov. 2009
Lösungsblatt 5
Datenbanksysteme I
Hinweis: Die in diesem Lösungsblatt vorgestellten SQL-Queries sind alle auf das Sporting Goods Schema anwendbar.
Bei der Ausführung der folgenden Musterlösungen ist jedoch zu beachten, dass die kursiven einfachen Anführungszeichen
durch die entsprechenden von SQL gewünschten einfachen Anführungszeichen ersetzt werden müssen.
Aufgabe 1: SQL-Queries
Lösungen:
a) Name aller Angestellten, die in Abteilung 41 arbeiten, alphabetisch sortiert nach Nachname:
SELECT last_name, first_name
FROM s_emp
WHERE dept_id=’41’
ORDER BY last_name, first_name;
b) Nachname und Vorname aller Angestellten samt Name der Abteilung, in der sie arbeiten sowie der dazugehörigen
Region:
SELECT s_emp.last_name, s_emp.first_name,
s_dept.name AS "DEP NAME", s_region.name AS "REGION"
FROM s_emp, s_dept, s_region
WHERE s_emp.dept_id = s_dept.id AND s_dept.region_id = s_region.id;
c) Namen aller Angestellten sowie deren dazugehöriger Manager:
SELECT e.last_name AS "Manager name", e.first_name AS "MANAGER FIRST NAME" ,
m.last_name , m.first_name
FROM s_emp e, s_emp m
WHERE e.id = m.manager_id;
d) Name aller Kunden, die noch keinem Kundenbetreuer zugeordnet wurden:
SELECT s_customer.name
FROM s_customer
WHERE s_customer.id NOT IN
(SELECT c.id
FROM s_customer c , s_emp e
WHERE c.sales_rep_id = e.id);
e) Liste aller Kunden mit GOOD oder EXCELLENT Kredit-Rating:
SELECT name
FROM s_customer
WHERE credit_rating=’GOOD’ OR credit_rating=’EXCELLENT’;
f) Daten aller Lager samt zugehöriger Manager plus Angestellte, die kein Manager eines Lagers sind:
SELECT w.id, w.address , w.city, w.state, w.country ,
e.last_name, e.first_name
FROM s_warehouse w RIGHT OUTER JOIN s_emp e
ON w.manager_id = e.id;
1
g) Namen und ID aller Mitarbeiter, deren Kommision unter 12 Prozent liegt:
SELECT e.id, e.last_name, e.first_name FROM s_emp e WHERE e.commission_pct<12;
Hier zeigt sich das Problem von Queries mit potentiellen NULL-Werten, da im Ergebnis nur Tupel vorkommen,
für die die im WHERE-Teil der Query formulierte Bedingung true ergibt. Für NULL-Werte in der Spalte COMMISSION PCT ergibt die Bedingung aber nach Seite 62 im Skript unknown. Somit liefert die Query keine Tupel
zurück, für die keine Kommission angegeben ist.
Aufgabe 2: Count-Bug
Lösungen:
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.
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 dept_nr FROM R)
MINUS
(SELECT dept_nr FROM R
WHERE job = ’clerk’
GROUP BY dept_nr
HAVING COUNT(*) > 2);
2
d) Ein ähnliches Phänomen lässt sich in der s inventory Relation beobachten. Gegeben sei dazu folgende Query:
SELECT warehouse_id
FROM s_inventory
WHERE product_id =’10013’ AND amount_in_stock <1
GROUP BY warehouse_id;
Gesucht werden hier alle Lager, in denen der Artikel mit Nummer 10013 nicht vorrätig ist, also auch diejenigen,
die den entsprechenden Artikel nicht führen. Die Query gibt jedoch ein leeres Ergebnis zurück, da Lager 101
noch 400 Exemplare des Artikels vorrätig hat, Lager 10501 noch 314. Dabei bleiben Lager 201, 301 und 401
unberücksichtigt, da diese den Artikel generell nicht im Sortiment haben. Laut Verbalisierung der gewünschten
Query sollten aber auch diese Lager im Ergebnis enthalten sein.
Dieses Beispiel verdeutlicht nochmals, dass der sogenannte Count-Bug nicht ein inhärentes Problem der CountFunktion ist, sondern vielmehr ein generelles Problem von SQL darstellt. Analog zu c) liefert folgende Query das
gewollte Ergebnis:
(SELECT warehouse_id FROM s_inventory)
MINUS
(SELECT warehouse_id FROM s_inventory
WHERE product_id=’10013’AND
amount_in_stock>1
GROUP BY warehouse_id);
Aufgabe 3: Aggregation und arithmetische Ausdrücke
Lösungen:
a) Durchschnittsgehalt der Firma:
SELECT AVG(salary) from s_emp;
b) Anzahl der Kunden pro Region:
SELECT r.name, count(DISTINCT c.id) AS "Number of Customers"
FROM s_customer c, s_region r
WHERE c.region_id = r.id
GROUP BY r.name;
c) Durchschnittsbestellbetrag und Name aller Kunden mit mehr als zwei Bestellungen:
SELECT c.id, c.name, AVG(o.total) AS "Durchschnittsbetrag"
FROM s_ord o, s_customer c
WHERE o.customer_id = c.id
GROUP BY c.id, c.name
HAVING count(DISTINCT o.id)>=2;
d) Der aktuelle Lagerbestand wird um 100 erhöht um anschließend halbiert, um den gewünschten Wert zu erreichen.
Zusätzlich wird die Differenz zwischen dem so vorherberechneten Ist-Wert und dem geforderten Soll-Wert (definbiert durch Reorder Point) ermittelt, um festzustellen, wo bei dieser Kalkulation Engpässe auftreten werden. Die
entsprechende Query lautet:
SELECT product_id, amount_in_stock , (amount_in_stock + 100)/2,
reorder_point, ((amount_in_stock + 100)/2 - reorder_point) AS "DIFFERENCE"
FROM s_inventory
WHERE warehouse_id=’201’ AND (product_id=’20106’ OR product_id=’20108’);
3
e) Monatliche Ausgaben bezüglich Gehalt für die einzelnen Zweige Administration, Finance, Operations und Sales:
SELECT d.name, sum(e.salary) AS "Monthly Salary Costs"
FROM s_dept d, s_emp e
WHERE e.dept_id = d.id
GROUP BY d.name
ORDER BY d.name;
f) Daten aller Bestellungen, die innerhalb von 10 Tagen nach Bestelleingang verschickt wurden:
SELECT * FROM s_ord WHERE date_shipped - date_ordered < 10;
4
Herunterladen