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:
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'; -- 2P1b) 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; -- 2PAlle 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; -- 2P1c) 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; -- 1PEine 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; -- 1P1d) 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); -- 2PVariante 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; -- 2PEs 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.5P2b) 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.5P3a) 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; -- 1PBEGIN 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;Links und weitere Informationen
SQL: Abfragen über mehrere Tabellen mit JOIN und Übungsaufgaben zu JOIN
SQL: Aggregierende Funktionen und Übungsaufgaben zu Aggregatfunktionen
als Nachschlagewerk: MariaDB Knowledge Base: START TRANSACTION
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]
