SQL-Übungsaufgaben zu JOINs, Aggregation, Unterabfragen und Transaktionen

https://bildung.social/@oerinformatik/112633708392604185

https://oer-informatik.de/sql-uebungsaufgaben

SQL, JOIN, GROUP BY, HAVING, Unterabfragen, ALL und ANY, Transaktionen, MariaDB, Übungsaufgaben, Prüfungsvorbereitung

tl/dr; (ca. 54 min Lese- und Bearbeitungszeit): Am Beispiel einer Rechnungsdatenbank erstellst Du SQL-Befehle mit Unterabfragen, Aggregationen, Joins und Transaktionen. Die Beispiellösungen und Erläuterungen können eingeblendet werden [AI-Assisted]. (Zuletzt geändert am 28.10.2026)

Dieser Artikel ist Teil einer Artikelreihe über SQL-Abfragen: Selektion und Projektion / Sortieren und Begrenzen (ORDER BY, LIMIT) / Funktionen im SELECT (Single-Row-Functions) / Fallunterscheidungen und NULL (CASE, COALESCE) / Aggregatfunktionen und Gruppierung / Abfragen über mehrere Tabellen (JOIN) / Übungsaufgaben: einfache Abfragen, Aggregatfunktionen, JOIN, Rechnungsdatenbank (gemischt)

Du möchtest eigene Aufgaben zum Üben von einem KI-Sprachmodell generieren lassen?

Kopiere den Text in ein KI-Sprachmodell Deiner Wahl und passe Kompetenzstufe, Anzahl, Bearbeitungszeit und Szenario bei Bedarf an. Wie die Aufgaben aufgebaut sind, beschreibt der Artikel Wie sind die Aufgaben aufgebaut?. Prüfe die Ergebnisse mit dem Lehrartikel: KI-Sprachmodelle machen Fehler.

Erstelle neue Übungsaufgaben zum Thema „SQL-Übungsaufgaben zu JOINs,
Aggregation, Unterabfragen und Transaktionen“.

Bauplan: Halte Dich an Aufbau, Kompetenzstufen, Operatoren,
Anforderungsbereiche, Punkte und Zeitschätzung, die im Artikel
https://oer-informatik.de/aufgaben-aufbau beschrieben sind.

Fachliche Grundlage:
- https://oer-informatik.de/sql-join
- https://oer-informatik.de/sql-groupfunctions
- Stilbeispiel: https://oer-informatik.de/sql-uebungsaufgaben

Die Aufgaben sollen Inhalte zu diesen Schlagwörtern abdecken:
- SQL
- JOIN
- GROUP BY
- HAVING
- Unterabfragen
- ALL und ANY
- Transaktionen
- MariaDB

Kompetenzstufe: Kernkompetenz (alternativ: Basiskompetenz, Profilkompetenz, Expertise)
Anzahl: 1 Aufgabe mit Teilaufgaben
Bearbeitungszeit in Minuten ca.: <5 bis 30 min>
Szenario: eines der Szenarien aus dem Bauplan

Vorgehen:
1. Lies zuerst die verlinkten Artikel. Kannst Du keine Webseiten
   abrufen, sag mir das, statt zu raten.
2. Steht bei einer Angabe noch ein Platzhalter in <…>, frag mich
   zuerst danach. Richte Punkte und Zahl der Teilaufgaben nach der
   Bearbeitungszeit (ca. 0,9 min je Punkt).
3. Gib je Aufgabe Titel mit Kompetenzstufe, Ausgangssituation und
   Teilaufgaben mit Operator und Punktzahl aus, dazu die geschätzte
   Bearbeitungszeit.
4. Gib die Beispiellösungen mit Punkteverteilung erst aus, wenn ich
   danach frage.

Übungsaufgabe Rechnungsdatenbank (Stufe 2 – Kernkompetenz)

Gegeben ist das folgende Datenmodell:

Relationenmodell für Rechnungen, Artikel, Rechnungspositionen und Adressen

1a) Selektion und Projektion (8 Punkte)

Erstelle eine Abfrage, die alle RechnungsNr des Kunden mit dem Namen „Fantasie GmbH“ ausgibt.

Beispielausgabe:

Name RechnungsNr
Fantasie GmbH 12345
Fantasie GmbH 56789
Fantasie GmbH 36789
SELECT K.Name AS Name, R.RechnungsNr AS RechnungsNr  -- 2P
FROM Rechnung AS R                                   -- 1P
INNER JOIN Kunde AS K ON R.KundenNr = K.KundenNr     -- 3P
WHERE K.Name = 'Fantasie GmbH';                      -- 2P

1b) Aggregieren (8 Punkte)

Erstelle eine Abfrage, die alle RechnungsNr ausgibt sowie, falls vorhanden, die jeweiligen Rechnungssummen (die Summe der zugehörigen GesamtpreisNetto, ohne Berücksichtigung der Rabatte).

Beispielausgabe:

RechnungsNr Rechnungssumme
12345 123,45
56789 2343,34
23223 NULL
36789 145,45

Damit auch Rechnungen ohne Positionen erscheinen (in der Beispielausgabe 23223 mit NULL), wird von Rechnung ausgehend ein LEFT JOIN gebildet, gruppiert und summiert:

SELECT R.RechnungsNr AS RechnungsNr,                                 -- 1P
       SUM(RP.GesamtpreisNetto) AS Rechnungssumme                    -- 2P
FROM Rechnung AS R                                                   -- 1P
LEFT JOIN RechnungsPosition AS RP ON R.RechnungsNr = RP.RechnungsNr  -- 2P
GROUP BY R.RechnungsNr;                                              -- 2P

Alle Spalten für die Summe stehen zwar schon in RechnungsPosition. Wer nur diese Tabelle gruppiert, erhält aber keine Zeile für Rechnungen ohne Positionen – die folgende Lösung ist deshalb nur teilweise richtig:

SELECT RechnungsNr,                                 -- 1P
       SUM(GesamtpreisNetto) AS Rechnungssumme      -- 2P
FROM RechnungsPosition                              -- 1P
GROUP BY RechnungsNr;                               -- 2P

1c) Bedingtes Aggregieren (8 Punkte)

Erstelle eine Abfrage, die Rechnungsnummer und Anzahl der zugehörigen Rechnungspositionen aller Rechnungen ausgibt, die mehr als 20 Rechnungspositionen haben. Die Liste soll nach Anzahl der Positionen absteigend sortiert sein.

Beispielausgabe:

RechnungsNr AnzahlPosition
12345 321
56789 200
36789 21

Analog zur vorigen Aufgabe: Alle nötigen Felder sind bereits in RechnungsPosition enthalten. Wichtig zur Reihenfolge der Klauseln:

  • Sie werden in der Reihenfolge des Merksatzes „Warum geht Herbert oft laufen?“ notiert (WHERE... GROUP... HAVING... ORDER BY... LIMIT...). Das gilt für MySQL/MariaDB.

  • Sie werden in der Reihenfolge des Merksatzes „Für wen geht Herbert so oft laufen?“ ausgeführt (FROM... WHERE... GROUP... HAVING... SELECT... ORDER BY... LIMIT...). Auch das gilt für MySQL/MariaDB.

Nach SQL-Standard ist der Alias AnzahlPosition des folgenden Befehls deshalb im HAVING-Clause noch nicht bekannt (dort wird COUNT(…) wiederholt; MariaDB/MySQL erlauben den Alias dort als Erweiterung), im ORDER BY-Clause kann er dagegen genutzt werden, weil dieser nach SELECT ausgeführt wird.

SELECT RechnungsNr,                                 -- 1P
       COUNT(RechnungsPositionNr) AS AnzahlPosition -- 2P
FROM RechnungsPosition                              -- 1P
GROUP BY RechnungsNr                                -- 1P
HAVING COUNT(RechnungsPositionNr) > 20              -- 2P
ORDER BY AnzahlPosition DESC;                       -- 1P

Eine Verknüpfung mit Rechnung verkompliziert die Lösung unnötig und ist weniger performant. Volle Punktzahl gibt es deshalb nur für die Lösung ohne JOIN; die folgende Variante erhält einen Punkt Abzug (höchstens 7 von 8 Punkten):

SELECT R.RechnungsNr AS RechnungsNr,                                 -- 0.5P
       COUNT(RP.RechnungsPositionNr) AS AnzahlPosition               -- 1P
FROM Rechnung AS R                                                   -- 0.5P
LEFT JOIN RechnungsPosition AS RP ON R.RechnungsNr = RP.RechnungsNr  -- 0P (unnötiger JOIN: 1 P Abzug)
GROUP BY R.RechnungsNr                                               -- 2P
HAVING COUNT(RP.RechnungsPositionNr) > 20                            -- 2P
ORDER BY AnzahlPosition DESC;                                        -- 1P

1d) Abfragen aus mehreren Tabellen (12 Punkte)

Erstelle eine Abfrage, die Kundennummern und Namen aller Kunden ausgibt, die Rechnungen mit mehr als 20 Rechnungspositionen haben. Alle Kunden sollen darin höchstens einmal vorkommen.

Beispielausgabe:

Kundennummer Name
3456 Fantasie GmbH
78987 Beispiel AG
671235 Muster KG

Hier gibt es (mindestens) zwei unterschiedliche Strategien: über JOIN alle Tabellen zusammentragen, Datensätze selektieren (HAVING) und die Ergebnismenge mit DISTINCT auf Unikate begrenzen oder die Selektion in einer Unterabfrage erledigen (was ich persönlich für eleganter halte):

Variante 1: Unterabfrage zur Datensatz-Selektion

SELECT K2.KundenNr AS Kundennummer, K2.Name AS Name                      -- 1P
FROM Kunde AS K2                                                         -- 1P
WHERE K2.KundenNr IN (                                                   -- 2P
   SELECT R.KundenNr                                                     -- 1P
   FROM Rechnung AS R                                                    -- 1P
   INNER JOIN RechnungsPosition AS RP ON R.RechnungsNr = RP.RechnungsNr  -- 2P
   GROUP BY R.RechnungsNr, R.KundenNr                                    -- 2P
   HAVING COUNT(RP.RechnungsPositionNr) > 20);                           -- 2P

Variante 2: Erst per JOIN maximale Information sammeln, die dann per HAVING und DISTINCT wieder eingedampft wird:

SELECT DISTINCT                                                       -- 2P
     K.KundenNr AS Kundennummer, K.Name AS Name                       -- 1P
FROM Rechnung AS R                                                    -- 1P
INNER JOIN RechnungsPosition AS RP ON R.RechnungsNr = RP.RechnungsNr  -- 2P
INNER JOIN Kunde AS K ON K.KundenNr = R.KundenNr                      -- 2P
GROUP BY R.RechnungsNr, K.KundenNr, K.Name                            -- 2P
HAVING COUNT(RP.RechnungsPositionNr) > 20;                            -- 2P

Es spielt hier keine Rolle, ob LEFT JOIN oder INNER JOIN verwendet wird, da ohnehin abgefragt wird, ob Rechnungspositionen vorhanden sind.

2a) Unterabfragen mit „vor jeder …“ (8 Punkte)

Erstelle eine Abfrage, die alle Rechnungsnummern ausgibt, deren Rechnung (nach numerischen Rechnungsnummern sortiert) vor jeder Rechnung der Fantasie GmbH liegt.

Beispielausgabe:

RechnungsNr
10234

Eigentlich bedeutet ja „nach numerischen Rechnungsnummern sortiert“, dass es eindeutig um die Nummern geht … das wäre dann so:

SELECT R.RechnungsNr AS RechnungsNr                 -- 1P
FROM Rechnung AS R                                  -- 1P
WHERE R.RechnungsNr < ALL (                          -- 2P
  SELECT R1.RechnungsNr                     -- 1P
  FROM Rechnung AS R1                               -- 1P
  INNER JOIN Kunde AS K ON R1.KundenNr = K.KundenNr -- 1P
  WHERE K.Name = 'Fantasie GmbH');                  -- 1P

… wer es jedoch falsch verstanden hat, der hat das „vor jeder Rechnung …“ als eine zeitliche Bedingung gelesen und den Sortierungs-Teil am Ende eingebaut:

SELECT R.RechnungsNr AS RechnungsNr                 -- 0.5P
FROM Rechnung AS R                                  -- 0.5P
WHERE R.Rechnungsdatum < ALL (                          -- 2P
  SELECT R1.Rechnungsdatum                  -- 1P
  FROM Rechnung AS R1                               -- 1P
  INNER JOIN Kunde AS K ON R1.KundenNr = K.KundenNr -- 0.5P
  WHERE K.Name = 'Fantasie GmbH')                   -- 0.5P
ORDER BY RechnungsNr;                               -- 0.5P

2b) Unterabfragen mit „vor einer …“ (8 Punkte)

Erstelle eine Abfrage, die Rechnungsnummern ausgibt, deren Rechnung (nach numerischen Rechnungsnummern sortiert) vor irgendeiner Rechnung der Fantasie GmbH liegt.

Beispielausgabe:

RechnungsNr
10234
12345
23223
36789

Analog zur vorigen Aufgabe:

SELECT R.RechnungsNr AS RechnungsNr                 -- 1P
FROM Rechnung AS R                                  -- 1P
WHERE R.RechnungsNr < ANY (                         -- 2P
  SELECT R1.RechnungsNr                     -- 1P
  FROM Rechnung AS R1                               -- 1P
  INNER JOIN Kunde AS K ON R1.KundenNr = K.KundenNr -- 1P
  WHERE K.Name = 'Fantasie GmbH');                  -- 1P

… bei falsch verstandener Bedingung (Teilpunkte)

SELECT R.RechnungsNr AS RechnungsNr                 -- 0.5P
FROM Rechnung AS R                                  -- 0.5P
WHERE R.Rechnungsdatum < ANY (                         -- 2P
  SELECT R1.Rechnungsdatum                  -- 1P
  FROM Rechnung AS R1                               -- 1P
  INNER JOIN Kunde AS K ON R1.KundenNr = K.KundenNr -- 0.5P
  WHERE K.Name = 'Fantasie GmbH')                   -- 0.5P
ORDER BY RechnungsNr;                               -- 0.5P

3a) Transaktionen starten (4 Punkte)

Es sollen in einem sicheren Transaktionsumfeld Veränderungen an der Datenbank vorgenommen werden, die sich noch nicht unmittelbar auswirken.

Erstelle alle Befehle, die nötig sind, um eine Transaktion zu starten und die (vorhandene) Rechnung „1234“ dem (vorhandenen) Kunden mit der KundenNr „234“ zuzuweisen.

START TRANSACTION;              -- 1P
UPDATE Rechnung                 -- 1P
SET KundenNr = 234              -- 1P
WHERE RechnungsNr = 1234;       -- 1P

BEGIN bzw. BEGIN WORK startet in MariaDB/MySQL ebenfalls eine Transaktion.

3b) Transaktionen abbrechen (2 Punkte)

Mit welchem Befehl werden die Befehle aus Aufgabe a nicht zur Anwendung gebracht und die Transaktion abgebrochen?

ROLLBACK;

3c) Transaktionen abschließen (2 Punkte)

Mit welchem Befehl werden die Befehle aus Aufgabe a persistent ausgeführt und die Transaktion abgeschlossen?

COMMIT;

Hinweis zur Nachnutzung als Open Educational Resource (OER)

Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: “SQL-Übungsaufgaben zu JOINs, Aggregation, Unterabfragen und Transaktionen” von oer-informatik.de (H. Stein), Lizenz: CC BY 4.0. Der Artikel wurde unter https://oer-informatik.de/sql-uebungsaufgaben veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/db-sql/sql. Erstellt mit KI-Assistenz; von mir umfassend redaktionell überarbeitet, geprüft und freigegeben. Stand: 28.10.2026.

[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: