\documentclass{bschlangaul-aufgabe}
\bLadePakete{syntax,rmodell}
\begin{document}
\bAufgabenMetadaten{
  Titel = {Aufgabe 4},
  Thematik = {Kundenverwaltungssystem},
  Referenz = 46116-2018-H.T1-TA1-A4,
  RelativerPfad = Examen/46116/2018/09/Thema-1/Teilaufgabe-1/Aufgabe-4.tex,
  ZitatSchluessel = db:pu:3,
  ZitatBeschreibung = {Aufgabe 2},
  BearbeitungsStand = mit Lösung,
  Korrektheit = unbekannt,
  Ueberprueft = {unbekannt},
  Stichwoerter = {SQL, SQL mit Übungsdatenbank, CREATE TABLE, HAVING, EXCEPT, Top-N-Query, WITH, DELETE},
  EinzelpruefungsNr = 46116,
  Jahr = 2018,
  Monat = 09,
  ThemaNr = 1,
  TeilaufgabeNr = 1,
  AufgabeNr = 4,
}

Gegeben\footcite[Aufgabe 2]{db:pu:3}\index{SQL} sind folgende Relationen
aus einem Kundenverwaltungssystem:
\footcite[Thema 1 Teilaufgabe 1 ]{examen:46116:2018:09}

\begin{bRelationenModell}
\bRelationMenge{Kunde}{\bPrimaer{ID}, Vorname, Nachname, PLZ}
\bRelationMenge{Produkt}{\bPrimaer{GTIN}, Bezeichnung, Bruttopreis, MWStSatz)}
\bRelationMenge{Kauf}{\bPrimaer{ID[Kunde], GTIN[Produkt], Datum}, Menge}
\end{bRelationenModell}

\begin{bAdditum}[Übungstabelle]
% Datenbankname: kundenverwaltungssystem
\begin{minted}{sql}
CREATE TABLE Kunde(
	ID INTEGER PRIMARY KEY,
	Vorname VARCHAR(30),
	Nachname VARCHAR(30),
	PLZ INTEGER
);

CREATE TABLE Produkt(
	GTIN INTEGER PRIMARY KEY,
	Bezeichnung VARCHAR(40),
	Bruttopreis NUMERIC(7,2),
	MWStSatz INTEGER
);

CREATE TABLE Kauf(
  ID INTEGER REFERENCES Kunde(ID),
  GTIN INTEGER REFERENCES Produkt(GTIN),
  Datum DATE,
  Menge INTEGER,
  PRIMARY KEY (ID, GTIN, Datum)
);

INSERT INTO Kunde VALUES
  (1, 'Max',  'Mustermann',  91052),
  (2, 'Erika', 'Musterfrau', 91052),
  (3, 'Max',   'Meyer',      91058),
  (4, 'Hans',  'Schmidt',    91054),
  (5, 'Eva',   'Müller',     91056),
  (6, 'Hanna', 'Winter',     20251),
  (7, 'Bert',  'Sommer',     20251),
  (8, 'Jakob', 'Sommer',     20251);

INSERT INTO Produkt VALUES
  (123, 'Buch', 12.30, 19),
  (124, 'Kaffee', 4.30, 7),
  (125, 'Pullover', 36.40, 19),
  (113, 'Heft', 2.30, 19),
  (023, 'Honig', 3.20, 7),
  (155, 'T-Shirt', 19.30, 19),
  (189, 'Nudeln', 1.30, 7),
  (004, 'Sonnenbrille', 40.60, 19),
  (324, 'Hammer', 22.80, 19),
  (112, 'Topf', 50.20, 19),
  (453, 'Klopapier', 3.30, 7),
  (765, 'Duschgel', 1.89, 19),
  (889, 'Deko', 5.89, 19);

INSERT INTO Kauf
  (ID, GTIN, Datum, Menge)
VALUES
  (1, 123, '2019-04-11', 1),
  (1, 124, '2019-04-11', 21),
  (1, 125, '2019-04-11', 1),
  (1, 765, '2019-04-11', 4),
  (1, 453, '2019-04-11', 1),
  (1, 324, '2019-04-11', 3),
  (1, 113, '2019-04-11', 2),
  (1, 023, '2019-04-11', 1),
  (1, 189, '2019-04-11', 1),
  (1, 112, '2019-04-11', 7),
  (1, 155, '2019-04-11', 7),
  (1, 004, '2019-05-11', 6),
  (7, 112, '2019-04-11', 7),
  (5, 112, '2019-04-11', 7),
  (8, 112, '2019-06-23', 5),
  (8, 112, '2019-04-12', 3),
  (2, 112, '2019-04-23', 1),
  (2, 112, '2019-08-11', 8),
  (4, 112, '2019-10-10', 2),
  (2, 453, '2019-09-24', 4),
  (4, 004, '2019-07-30', 9);
\end{minted}
\index{SQL mit Übungsdatenbank}
\end{bAdditum}

Verwenden Sie im Folgenden nur Standard-SQL und keine
produktspezifischen Erweiterungen. Sie dürfen bei Bedarf Views anlegen.
Geben Sie einen Datensatz, also eine Entity, nicht mehrfach aus.

\begin{enumerate}

%%
% (a)
%%

\item Schreiben Sie eine SQL-Anweisung, die die Tabelle \emph{„Kauf“}
anlegt. Gehen Sie davon aus, dass die Tabellen \emph{„Kunde“} und
\emph{„Produkt“} bereits existieren.\index{CREATE TABLE}

\begin{bAntwort}
\begin{minted}{sql}
CREATE TABLE IF NOT EXISTS Kauf (
  ID INTEGER REFERENCES Kunde(ID),
  GTIN INTEGER REFERENCES Produkt(GTIN),
  Datum DATE,
  Menge INTEGER,
  PRIMARY KEY (ID, GTIN, Datum)
);
\end{minted}
\end{bAntwort}

%%
% (b)
%%

\item Schreiben Sie eine SQL-Anweisung, die \emph{Vorname} und
\emph{Nachname} aller \emph{Kunden} mit der \emph{Postleitzahl}
\emph{20251} ausgibt, absteigend sortiert nach \emph{Nachname} und bei
gleichen \emph{Nachnamen}, absteigend nach \emph{Vorname}.

\begin{bAntwort}
\begin{minted}{sql}
SELECT Vorname, Nachname
FROM Kunde
WHERE PLZ = 20251
ORDER BY Nachname DESC, Vorname DESC;
\end{minted}

\begin{verbatim}
 vorname | nachname
---------+----------
 Hanna   | Winter
 Jakob   | Sommer
 Bert    | Sommer
(3 rows)
\end{verbatim}
\end{bAntwort}

%%
% (c)
%%

\item Schreiben Sie eine SQL-Anweisung, die zu jedem Einkauf mit mehr
als 10 unterschiedlichen Produkten den \emph{Nachnamen} des
\emph{Kunden} und den \emph{Bruttogesamtpreis} des Einkaufs ausgibt. Ein
Einkauf ist definiert als Menge aller Produkte, die ein bestimmter Kunde
an einem bestimmten Datum kauft.

\begin{bAntwort}
\begin{minted}{sql}
SELECT Nachname, SUM(Bruttopreis * Menge)
FROM Kunde k, Produkt p, Kauf x
WHERE k.ID = x.ID AND p.GTIN = x.GTIN
GROUP BY Datum, Nachname, k.ID
HAVING COUNT (*) > 10;
\end{minted}

\begin{verbatim}
  nachname  |  sum
------------+--------
 Mustermann | 713.86
(1 row)
\end{verbatim}
\end{bAntwort}

%%
% (d)
%%

\item Schreiben Sie eine SQL-Anweisung, die die \emph{GTINs} aller
Produkte ausgibt, die an mindestens einen in der Datenbank enthaltenen
PLZ-Bereich noch nie verkauft worden sind. Als in der Datenbank
enthaltener PLZ-Bereich gelten alle in der Tabelle \emph{„Kunde“}
enthaltenen PLZs. Ein Produkt gilt als an einen PLZ-Bereich verkauft,
sobald es von mindestens einem Kunden aus diesem PLZ-Bereich gekauft
wurde. Produkte, die bisher noch gar nicht verkauft worden sind, müssen
nicht berücksichtigt werden.\index{HAVING}\index{EXCEPT}

\begin{bAntwort}
Die beiden Lösungswege liefern leider unterschiedliche Ergebnisse.
\begin{minted}{sql}
WITH tmp AS (
  SELECT x.GTIN, k.PLZ
  FROM Kunde k, Kauf x
  WHERE x.ID = k.ID
  GROUP BY x.GTIN, k.PLZ
)

SELECT DISTINCT GTIN
FROM tmp
WHERE EXISTS (
  SELECT Kunde.PLZ
  FROM Kunde LEFT OUTER JOIN tmp
  ON Kunde.PLZ = tmp.PLZ
  WHERE tmp.PLZ IS NULL
)
ORDER BY GTIN;
\end{minted}

\begin{verbatim}
 gtin
------
    4
   23
  112
  113
  123
  124
  125
  155
  189
  324
  453
  765
(12 rows)
\end{verbatim}

oder

\begin{minted}{sql}
SELECT DISTINCT GTIN FROM (
  (
    SELECT GTIN, PLZ
    FROM Kunde, Produkt
  )
  EXCEPT
  (
    SELECT x.GTIN, k.PLZ
    FROM Kunde k, Kauf x
    WHERE x.ID = k.ID
    GROUP BY x.GTIN, k.PLZ
  )
) as tmp
ORDER BY GTIN;
\end{minted}

\begin{verbatim}
 gtin
------
    4
   23
  112
  113
  123
  124
  125
  155
  189
  324
  453
  765
  889
(13 rows)
\end{verbatim}
\end{bAntwort}

%%
% (e)
%%

\item Schreiben Sie eine SQL-Anweisung, die die Top-Ten der am meisten
verkauften Produkte ausgibt. Ausgegeben werden sollen der Rang (1 bis
10) und die Bezeichnung des Produkts. Gehen Sie davon aus, dass es keine
zwei Produkte mit gleicher Verkaufszahl gibt und verwenden Sie keine
produktspezifischen Anweisungen wie beispielsweise \verb|ROWNUM|,
\verb|TOP| oder \verb|LIMIT|.\index{Top-N-Query}\index{WITH}

\begin{bAntwort}
\begin{minted}{sql}
WITH Gesamtverkauf AS (
  SELECT k.GTIN, Bezeichnung, SUM(Menge) AS Gesamtmenge
  FROM Produkt p, Kauf k
  WHERE p.GTIN = k.GTIN
  GROUP BY k.GTIN, Bezeichnung
)

SELECT g1.Bezeichnung, COUNT (*) AS Rang
FROM Gesamtverkauf g1, Gesamtverkauf g2
WHERE g1.Gesamtmenge <= g2.Gesamtmenge
GROUP BY g1.GTIN, g1.Bezeichnung
HAVING COUNT (*) <= 10
ORDER BY Rang;
\end{minted}

\begin{verbatim}
 bezeichnung  | rang
--------------+------
 Topf         |    1
 Kaffee       |    2
 Sonnenbrille |    3
 T-Shirt      |    4
 Klopapier    |    5
 Duschgel     |    6
 Hammer       |    7
 Heft         |    8
(8 rows)
\end{verbatim}
\end{bAntwort}

%%
% (f)
%%

\item Schreiben Sie eine SQL-Anweisung, die alle Produkte löscht, die
noch nie gekauft wurden.\index{DELETE}

\begin{bAntwort}
\begin{verbatim}
 count
-------
    13
(1 row)
\end{verbatim}

\begin{minted}{sql}
SELECT COUNT(*) FROM Produkt;

DELETE FROM Produkt
WHERE GTIN NOT IN
(
  SELECT DISTINCT GTIN
  FROM Kauf
);

SELECT COUNT(*) FROM Produkt;
\end{minted}

\begin{verbatim}
 count
-------
    12
(1 row)
\end{verbatim}
\end{bAntwort}
\end{enumerate}

\end{document}
