\documentclass{bschlangaul-aufgabe} \bLadePakete{sql,rmodell,relationale-algebra} \begin{document} \bAufgabenMetadaten{ Titel = {Aufgabe 2}, Thematik = {Mode-Kollektionen}, Referenz = 66116-2020-F.T2-TA2-A2, RelativerPfad = Examen/66116/2020/03/Thema-2/Teilaufgabe-2/Aufgabe-2.tex, ZitatSchluessel = examen:66116:2020:03, BearbeitungsStand = mit Lösung, Korrektheit = unbekannt, Ueberprueft = {unbekannt}, Stichwoerter = {SQL, SQL mit Übungsdatenbank}, EinzelpruefungsNr = 66116, Jahr = 2020, Monat = 03, ThemaNr = 2, TeilaufgabeNr = 2, AufgabeNr = 2, } Gegeben sei der folgende Ausschnitt eines Schemas für die Verwaltung von Kollektionen: \index{SQL} \footcite{examen:66116:2020:03} % Promi : {| % Name : VARCHAR(255), % Alter : INTEGER, % Wohnort : VARCHAR.(255) % Kleidungsstueck : {| % ID : INTEGER, % Hauptbestandteil : VARCHAR(255), % gehoert\_zu : VARCHAR.(255) % Kollektion : {| % Name : VARCHAR(255), % Jahr : INTEGER, % Saison : VARCHAR(255) % hat\_getragen : {[ % ]} PromiName : VARCHAR.(355), % KleidungsstuecklD : INTEGER, % Datum : DATE % 1} % |} % promotet: {| % PromiName : VARCHAR(255), % KollektionName : VARCHAR(255) Die Tabelle \emph{Promi} beschreibt Promis über ihren eindeutigen Namen, ihr Alter und ihren Wohnort. Kollektion enthält Informationen über \emph{Kollektionen}, nämlich deren eindeutigen Namen, das Jahr und die Saison. Die Tabelle \emph{promotet} verwaltet über Referenzen, welcher Promi welche Kollektion promotet. \emph{Kleidungsstück} speichert die IDs von Kleidungsstücken zusammen mit dem Hauptbestandteil und einer Referenz auf die zugehörige Kollektion. Die Tabelle \emph{hat\_getragen} verwaltet über Referenzen, welcher Promi welches Kleidungsstück an welchem Datum getragen hat. \bigskip Beachten Sie bei der Formulierung der SQL-Anweisungen, dass die Ergebnisrelationen keine Duplikate enthalten dürfen. Sie dürfen geeignete Views definieren. % Datenbankname: kollektion \begin{minted}{sql} CREATE TABLE Promi ( Name VARCHAR(255) PRIMARY KEY, Alter INTEGER, Wohnort VARCHAR(255) ); CREATE TABLE Kollektion ( Name VARCHAR(255) PRIMARY KEY, Jahr INTEGER, Saison VARCHAR(255) ); CREATE TABLE Kleidungsstueck ( ID INTEGER PRIMARY KEY, Hauptbestandteil VARCHAR(255), gehoert_zu VARCHAR(255) REFERENCES Kollektion(Name) ); CREATE TABLE hat_getragen ( PromiName VARCHAR(255) REFERENCES Promi(Name), KleidungsstueckID INTEGER REFERENCES Kleidungsstueck(ID), Datum DATE, PRIMARY KEY(PromiName, KleidungsstueckID, Datum) ); CREATE TABLE promotet ( PromiName VARCHAR(255), KollektionName VARCHAR(255), PRIMARY KEY(PromiName, KollektionName) ); INSERT INTO Promi (Name, Alter, Wohnort) VALUES ('Till Schweiger', 52, 'Dortmund'), ('Lena Meyer-Landrut', 30, 'Hannover'); INSERT INTO Kollektion VALUES ('Gerry Weber', 2020, 'Sommer'), ('H & M', 2020, 'Sommer'); INSERT INTO promotet (PromiName, KollektionName) VALUES ('Till Schweiger', 'Gerry Weber'), ('Lena Meyer-Landrut', 'H & M'); INSERT INTO Kleidungsstueck (ID, Hauptbestandteil, gehoert_zu) VALUES (1, 'Hose', 'Gerry Weber'); INSERT INTO hat_getragen (PromiName, KleidungsstueckID, Datum) VALUES ('Till Schweiger', 1, '2021-08-03'); \end{minted} \index{SQL mit Übungsdatenbank} \begin{enumerate} %% % 1. %% \item Schreiben Sie SQL-Anweisungen, welche die Tabelle \texttt{hat\_getragen} inklusive aller benötigten Fremdschlüsselconstraints anlegt. Erläutern Sie kurz, warum die Spalte \texttt{Datum} Teil des Primärschlüssels ist. \begin{bAntwort} \begin{minted}{sql} CREATE TABLE IF NOT EXISTS hat_getragen ( PromiName VARCHAR(255) REFERENCES Promi(Name), KleidungsstueckID INTEGER REFERENCES Kleidungsstueck(ID), Datum DATE, PRIMARY KEY(PromiName, KleidungsstueckID, Datum) ); \end{minted} Das Datenbanksystem achtet selbst darauf, dass Felder des Primärschlüssels nicht NULL sind. Deshalb muss man NOT NULL bei keinem der drei Felder angeben. \end{bAntwort} %% % 2. %% \item Schreiben Sie eine SQL-Anweisung, welche die Namen der Promis ausgibt, die eine Sommer-Kollektion promoten (Saison ist „Sommer“). \begin{bAntwort} \begin{minted}{sql} SELECT p.PromiName FROM promotet p, Kollektion k WHERE k.Saison = 'Sommer' AND p.KollektionName = k.Name; \end{minted} \begin{minted}{md} prominame -------------------- Till Schweiger Lena Meyer-Landrut (2 rows) \end{minted} \end{bAntwort} %% % 3. %% \item Schreiben Sie eine SQL-Anweisung, die die Namen aller Promis und der Kollektionen bestimmt, welche der Promi zwar promotet, aber daraus noch kein Kleidungsstück getragen hat. \begin{bAntwort} Lösung mit NOT IN: \begin{minted}{sql} SELECT * FROM promotet p WHERE p.KollektionName NOT IN ( SELECT k.Name FROM Kollektion k INNER JOIN Kleidungsstueck s ON s.gehoert_zu = K.Name INNER JOIN hat_getragen t ON t.KleidungsstueckID = s.ID WHERE t.PromiName = p.PromiName ); \end{minted} Mit EXCEPT: \begin{minted}{sql} ( SELECT p.PromiName, p.KollektionName FROM promotet p ) EXCEPT ( SELECT p.PromiName, p.KollektionName FROM promotet p INNER JOIN Kollektion k ON p.KollektionName = k.Name INNER JOIN Kleidungsstueck s ON s.gehoert_zu = k.Name INNER JOIN hat_getragen t ON t.KleidungsstueckID = s.ID WHERE t.PromiName = p.PromiName ); \end{minted} \end{bAntwort} %% % 4. %% \item Bestimmen Sie für die folgenden SQL-Anweisungen die minimale und maximale Anzahl an Tupeln im Ergebnis. Beziehen Sie sich dabei auf die Größe der einzelnen Tabellen. Verwenden Sie für die Lösung folgende Notation: -- Promi -- beschreibt die Größe der Tabelle Promi. \begin{enumerate} %% % a) %% \item \strut \begin{minted}{sql} SELECT k.Name FROM Kollektion k, Kleidungsstueck kl WHERE k.Name = kl.gehoert_zu and k.Jahr = 2018 GROUP BY k.Name HAVING COUNT(kl.Hauptbestandteil) > 10; \end{minted} \begin{bAntwort} -- Kollektion -- beschreibt die Anzahl der Tupel in der Tabelle Kollektion. Die minimale Anzahl an Tupeln im Ergebnis ist 0, da es sein kann, dass keine Kollektion den Anforderungen \bSqlCode{k.Jahr = 2018} oder \bSqlCode{HAVING COUNT(...)} genügt. Die maximale Anzahl an Tupeln im Ergebnis ist -- Kollektion --, da nur Namen aus Kollektion ausgewählt werden. \end{bAntwort} %% % b) %% \item \strut \begin{minted}{sql} SELECT DISTINCT k.Jahr FROM Kollektion k WHERE k.Name IN ( SELECT pr.KollektionName FROM Promi p, promotet pr WHERE p.Alter < 30 AND pr.PromiName = p.Name ); \end{minted} \begin{bAntwort} Die minimale Anzahl an Tupeln im Ergebnis ist 0, da es sein kann, dass keine Kollektion promotet wird. Die maximale Anzahl ist das Minimum von -- Kollektion -- und 30, da die Promis, die Kollektionen beworben haben, die älter als 30 Jahre sind, selbst mindestens 30 Jahre alt sein müssen. Zugrundeliegende Annahmen: Kollektionen werden nur im Erscheinungsjahr von Promis beworben; Neugeborene, die Kollektionen bewerben, werden ggf. Promis. \end{bAntwort} \end{enumerate} %% % 5. %% \item Beschreiben Sie den Effekt der folgenden SQL-Anfrage in natürlicher Sprache \begin{minted}{sql} SELECT pr.KollektionName FROM promotet pr, Promi p WHERE pr.PromiName = p.Name GROUP BY pr.KollektionName HAVING COUNT (*) IN ( SELECT MAX(anzahl) FROM ( SELECT k.Name, COUNT(*) AS anzahl FROM Kollektion k, promotet pr WHERE k.Name = pr.KollektionName GROUP BY k.Name ) as tmp ); \end{minted} \begin{bAntwort} Die Abfrage gibt die Namen aller Kollektionen aus, bei denen die Anzahl der bewerbenden Promis am größten ist. \end{bAntwort} %% % 6. %% \item Formulieren Sie folgende SQL-Anfrage in relationaler Algebra. Die Lösung kann in Baum- oder in Term-Schreibweise angegeben werden, wobei eine Schreibweise genügt. \begin{minted}{sql} SELECT p.Wohnort FROM Promi p, promotet pr, Kollektion k WHERE p.Name = pr.PromiName AND k.Name pr.KollektionName AND k.Jahr 2018; \end{minted} \begin{enumerate} %% % a) %% \item Konvertieren Sie zunächst die gegebene SQL-Anfrage in die zugehörige Anfrage in relationaler Algebra nach Standard-Algorithmus. \begin{bAntwort} \begin{multline*} \pi_{\text{Promi.Wohnort}} (\\ \sigma_{ \text{Promi.Name} = \text{promotet.PromiName} \land \text{Kollektion.Name} = \text{promotet.KollektionName} \land \text{Kollektion.Jahr} = 2018 } \\(\text{Promi} \times \text{promotet} \times \text{Kollektion}) ) \end{multline*} \end{bAntwort} %% % b) %% \item Führen Sie anschließend eine relationale Optimierung durch. Beschreiben und begründen Sie dabei kurz jeden durchgeführten Schritt. \begin{bAntwort} \begin{enumerate} \item[1. Schritt:] Um die kartesischen Produkte in Joins zu verwandeln, muss die Selektion in drei Selektionen zerlegt werden. \begin{multline*} \pi_{\text{Promi.Wohnort}} (\\ \sigma_{\text{Promi.Name} = \text{promotet.PromiName}}(\\ \sigma_{\text{Kollektion.Name} = \text{promotet.KollektionName}} (\\ \sigma_{\text{Kollektion.Jahr} = 2018} (\text{Promi} \times (\text{promotet} \times\\ \text{Kollektion}\\ )\\ )\\ )\\ ) \end{multline*} \item[2. Schritt:] Man kann die Selektion nach dem Jahr ausführen, bevor die kartesischen Produkte ausgeführt werden. Dadurch werden von vornherein nur die Kollektionen betrachtet, die in dem entsprechenden Jahr aufgetreten sind. \begin{multline*} \pi_{\text{Promi.Wohnort}} (\sigma Promi.Name = promotet.PromiName (\sigma Kollektion.Name = promotet.KollektionName (Promi \times (promotet \times \sigma Kollektion.Jahr = 2018 (Kollektion)))) \end{multline*} \item[3. Schritt:] Die zweitinnerste Selektion kann direkt nach dem innersten kartesischen Produkt ausgeführt werden; dadurch wird das Gesamtprodukt nicht so groß. Man benötigt also weniger Rechenzeit und Speicher. \begin{multline*} \pi_{\text{Promi.Wohnort}} (\sigma Promi.Name = promotet.PromiName (Promi \times \sigma Kollektion.Name = promotet.KollektionName (promotet \times \sigma Kollektion.Jahr = 2018 (Kollektion))) \end{multline*} \item[4. Schritt:] Beide Selektionen in Verbindung mit dem jeweiligen kartesischen Produkt können in einen Join verwandelt werden. So wird nicht zuerst das gesamte Produkt berechnet und danach die passenden Eintraege ausgewaehlt, sondern gleich nur die zusammengehörigen Eintraege kombiniert. Auch dies spart Rechenzeit und Speicher. \begin{multline*} \pi_{\text{Promi.Wohnort}} (Promi \bowtie Promi.Name = promotet.PromiName (promotet \bowtie Kollektion.Name = promotet.KollektionName \sigma Kollektion.Jahr = 2018 (Kollektion))) \end{multline*} \end{enumerate} \end{bAntwort} \end{enumerate} \end{enumerate} \end{document}