\documentclass{bschlangaul-aufgabe}
\bLadePakete{mathe,syntax}
\usepackage{fancyvrb}
\begin{document}
\bAufgabenMetadaten{
  Titel = {Aufgabe 2},
  Thematik = {Schulverwaltung},
  Referenz = 66116-2016-H.T2-TA1-A2,
  RelativerPfad = Examen/66116/2016/09/Thema-2/Teilaufgabe-1/Aufgabe-2.tex,
  ZitatSchluessel = examen:66116:2016:09,
  BearbeitungsStand = mit Lösung,
  Korrektheit = unbekannt,
  Ueberprueft = {unbekannt},
  Stichwoerter = {SQL mit Übungsdatenbank, SQL, CREATE TABLE, CONSTRAINT, ALTER TABLE, GROUP BY, HAVING},
  EinzelpruefungsNr = 66116,
  Jahr = 2016,
  Monat = 09,
  ThemaNr = 2,
  TeilaufgabeNr = 1,
  AufgabeNr = 2,
}

Gegeben\footcite{examen:66116:2016:09} sei der folgende Ausschnitt aus
dem Schema einer Schulverwaltung:
\footcite[2. SQL und relationale Algebra]{db:ab:examen-gym-2016-09}

\newcommand{\tmpu}[1]{\underline{\texttt{#1}}}

\begin{Verbatim}[commandchars=+*~]
Person : {[
  +tmpu*ID~ : INTEGER,
  Name : VARCHAR(255),
  Wohnort : VARCHAR(255),
  Typ : CHAR(1)
]}

Unterricht : {[
  +tmpu*Klassenbezeichnung~ : VARCHAR(20),
  +tmpu*Schuljahr~ : INTEGER,
  +tmpu*Lehrer~ : INTEGER,
  +tmpu*Fach~ : VARCHAR(100)
]}

Klasse : {[
  +tmpu*Klassenbezeichnung~ : VARCHAR(20),
  +tmpu*Schuljahr~ : INTEGER,
  Klassenlehrer : INTEGER
]}

Klassenverband : {[
  +tmpu*Schüler~ : INTEGER,
  Klassenbezeichnung : VARCHAR(20),
  +tmpu*Schuljahr~ : INTEGER
]}
\end{Verbatim}

% Datenbankname: schulverwaltung
\begin{minted}{sql}
CREATE TABLE Person (
  ID INTEGER PRIMARY KEY,
  Name VARCHAR(255),
  Wohnort VARCHAR(255),
  Typ CHAR(1) CHECK(Typ in ('S', 'L'))
);

CREATE TABLE Klasse (
  Klassenbezeichnung VARCHAR(20),
  Schuljahr INTEGER,
  Klassenlehrer INTEGER REFERENCES Person(ID),
  PRIMARY KEY (Klassenbezeichnung, Schuljahr)
);

CREATE TABLE Klassenverband (
  Schüler INTEGER REFERENCES Person(ID),
  Klassenbezeichnung VARCHAR(20),
  Schuljahr INTEGER,
  PRIMARY KEY (Schüler, Schuljahr)
);

CREATE TABLE Unterricht (
  Klassenbezeichnung VARCHAR(20),
  Schuljahr INTEGER,
  Lehrer INTEGER REFERENCES Person(ID),
  Fach VARCHAR(100),
  CONSTRAINT Unterricht_PK
    PRIMARY KEY (Klassenbezeichnung, Schuljahr, Lehrer, Fach)
);

INSERT INTO Person VALUES
  (1, 'Lehrer Ludwig', 'München', 'L'),
  (2, 'Schüler Max', 'München', 'S'),
  (3, 'Schülerin Maria', 'München', 'S'),
  (4, 'Schülerin Eva', 'Starnberg', 'S'),
  (5, 'Lehrerin Walter', 'München', 'L'),
  (6, 'Schüler Karl', 'München', 'S');

INSERT INTO Klasse VALUES
  ('1a', 2015, 1),
  ('1a', 2014, 1),
  ('1b', 2015, 5);

INSERT INTO Klassenverband VALUES
  (2, '1a', 2015),
  (3, '1a', 2015),
  (4, '1b', 2015),
  (6, '1a', 2015),
  (6, '1a', 2014);
\end{minted}
\index{SQL mit Übungsdatenbank}

\noindent
Hierbei enthält die Tabelle \emph{Person} Informationen über Lehrer (Typ
’L’) und Schüler (Typ ’S’); andere Werte für Typ sind nicht zulässig.
\emph{Klasse} beschreibt die Klassen, die in jedem Schuljahr gebildet
wurden, zusammen mit ihrem Klassenlehrer. In \emph{Unterricht} wird
abgelegt, welcher Lehrer welches Fach in welcher Klasse unterrichtet; es
ist möglich, dass derselbe Lehrer mehr als ein Fach in einer Klasse
unterrichtet. \emph{Klassenverband} beschreibt die Zuordnung der Schüler
zu den Klassen.

\begin{enumerate}

%%
% a)
%%

\item Schreiben Sie eine SQL-Anweisung\index{SQL}, die die Tabelle
\emph{Unterricht} mit allen ihren Constraints (einschließlich
Fremdschlüsselconstraints) anlegt\index{CREATE TABLE}.

\begin{bAntwort}
\begin{minted}{sql}
CREATE TABLE IF NOT EXISTS Person(
  ID INTEGER PRIMARY KEY,
  Name VARCHAR(255),
  Wohnort VARCHAR(255),
  Typ CHAR(1) CHECK(Typ in ('S', 'L'))
);

CREATE TABLE IF NOT EXISTS Klasse(
  Klassenbezeichnung VARCHAR(20),
  Schuljahr INTEGER,
  Klassenlehrer INTEGER REFERENCES Person(ID),
  PRIMARY KEY (Klassenbezeichnung, Schuljahr)
);

CREATE TABLE IF NOT EXISTS Unterricht (
  Klassenbezeichnung VARCHAR(20) REFERENCES Klasse(Klassenbezeichnung),
  Schuljahr INTEGER REFERENCES Klasse(Schuljahr),
  Lehrer INTEGER REFERENCES Person(ID),
  Fach VARCHAR(100),
  CONSTRAINT Unterricht_PK
    PRIMARY KEY (Klassenbezeichnung, Schuljahr, Lehrer, Fach)
);
\end{minted}
\end{bAntwort}

%%
% b)
%%

\item Definieren Sie ein geeignetes Constraint, das sicherstellt, dass
nur zulässige Werte im Attribut Typ der (bereits angelegten) Tabelle
\emph{Person} eingefügt werden können.\index{CONSTRAINT}\index{ALTER
TABLE}

\begin{bAntwort}
Ich habe \verb|REFERENCES| bei Unterricht Schuljahr vergessen, die
referenzierten Tabellen \verb|Person| und \verb|Klasse| wurden in der
Musterlösung auch nicht angelegt.

\begin{minted}{sql}
ALTER TABLE Person
  ADD CONSTRAINT TypLS
    CHECK(Typ IN ('S', 'L'));
\end{minted}
\end{bAntwort}

%%
% c)
%%

\item Schreiben Sie eine SQL-Anweisung, die die Bezeichnung der Klassen
bestimmt, die im Schuljahr 2015 die meisten Schüler haben.\index{GROUP BY}

\begin{bAntwort}
Falsch: \verb|ORDER BY Anzahl;|. \verb|DESC| vergessen.

\begin{minted}{sql}
SELECT k.Klassenbezeichnung, COUNT(*) AS Anzahl
FROM Klasse k, Klassenverband v
WHERE
  k.Schuljahr = 2015 AND
  k.Klassenbezeichnung = v.Klassenbezeichnung
GROUP BY k.Klassenbezeichnung
ORDER BY COUNT(*) DESC;
\end{minted}
\end{bAntwort}

%%
% d)
%%

\item Schreiben Sie eine SQL-Anweisung, die die Namen aller Lehrer
bestimmt, die nur Schüler aus ihrem Wohnort unterrichtet haben.

\begin{bAntwort}
\begin{minted}{sql}
SELECT DISTINCT l.Name
FROM Person l
WHERE NOT EXISTS(
  SELECT DISTINCT *
  FROM Unterricht u, Klassenverband v, Person s
  WHERE
    u.Lehrer = l.ID AND
    u.Klassenbezeichnung = v.Klassenbezeichnung AND
    v.Schüler = s.ID AND
    l.Wohnort != s.Wohnort
);
\end{minted}
\end{bAntwort}

%%
% e)
%%

\item Schreiben Sie eine SQL-Anweisung, die die Namen aller Schüler
bestimmt, die immer den gleichen Klassenlehrer hatten.\index{HAVING}

\begin{bAntwort}
\begin{minted}{sql}
SELECT s.Name
FROM Person s, Klasse k, Klassenverband v
WHERE
  v.Klassenbezeichnung = k.Klassenbezeichnung AND
  k.Schuljahr = v.Schuljahr AND
  v.Schüler = s.ID
GROUP BY s.ID, s.Name
HAVING COUNT(*) = 1
\end{minted}
\end{bAntwort}

%%
% f)
%%

\item Schreiben Sie eine SQL-Anweisung, die alle Paare von Schülern
bestimmt, die mindestens einmal in der gleichen Klasse waren. Es genügt
dabei, wenn Sie die ID der Schüler bestimmen.

\begin{bAntwort}
\begin{minted}{sql}
SELECT DISTINCT s1.ID, s2.ID
FROM Klassenverband s1, Klassenverband s2
WHERE
  s1.Schuljahr = s2.Schuljahr AND
  s1.Schueler <> s2.Schueler AND
  s1.Klassenbezeichung = s2.Klassenbezeichnung;
\end{minted}
\end{bAntwort}

%%
% g)
%%

\item Formulieren Sie eine Anfrage in der relationalen Algebra, die die
ID aller Schüler bestimmt, die mindestens einmal von „Ludwig Lehrer“
unterrichtet wurden.

\begin{bAntwort}
\begin{multline*}
\pi_{\text{Schüler}} (\\
  \sigma_{\text{Name} = \mlq \text{Ludwig Lehrer} \mrq} (\text{Person})\\
  \bowtie_{\text{Person.ID} = \text{Unterricht.Lehrer}}\\
  \text{Unterricht}\\
  \bowtie_{\text{Unterricht.Klassenbezeichnung} = \text{Klassenverband.Klassenbezeichnung}}\\
  \text{Klassenverband}\\
)
\end{multline*}
\end{bAntwort}

%%
% h)
%%

\item Formulieren Sie eine Anfrage in der relationalen Algebra, die
Namen und ID der Schüler bestimmt, die von allen Lehrern unterrichtet
wurden.

\begin{bAntwort}
\begin{multline*}
\pi_{\text{Name,ID}}(\\
  (\\
    \pi_{\text{Lehrer,Schueler}}(
      \text{Unterricht}
      \bowtie
      \text{Klassenverband}
    )
    \div
    \pi_{\text{ID}}(
      \sigma_{\text{Typ} = \mlq \text{L} \mrq }(\text{Person})
    )\\
  )\\
  \bowtie\\
  \text{Person}\\
)
\end{multline*}
\end{bAntwort}

\end{enumerate}

\noindent
Beachten Sie bei der Formulierung der SQL-Anfragen, dass die
Ergebnisrelationen keine Duplikate enthalten dürfen. Sie dürfen
geeignete Views definieren.

\end{document}
