Klausuraufgaben
14 PunkteAufgabe 1: DDL und Integrität
Erstellen Sie die Tabelle artist_contract für Verträge zwischen Künstlern und Labels.
| Attribut |
Anforderung |
| contract_id | automatisch erzeugter Primärschlüssel |
| artist_id | Pflichtfeld, Fremdschlüssel auf artist(artist_id); beim Löschen des Künstlers sollen Verträge mitgelöscht werden |
| label_name | Pflichtfeld, maximal 80 Zeichen |
| starts_on | Pflichtfeld |
| ends_on | optional, darf nicht vor starts_on liegen |
| royalty_rate | Zahl zwischen 0 und 0,5 einschließlich 0,5, aber größer als 0 |
| exclusive | Pflichtfeld, Standardwert true |
- Schreiben Sie den vollständigen
CREATE TABLE-Befehl inklusive sinnvoll benannter Constraints. Ein Künstler darf beim selben Label nicht zweimal am selben Startdatum beginnen. 9 Punkte
- Schreiben Sie einen
ALTER TABLE-Befehl, der für album sicherstellt, dass (artist_id, title) eindeutig ist. Nennen Sie ein praktisches Risiko beim Ausführen auf Bestandsdaten. 3 Punkte
- Entscheiden Sie für die folgenden Einfügungen, ob sie zulässig sind. Begründen Sie kurz. Gehen Sie davon aus, dass
artist_id = 7 existiert und artist_id = 999 nicht existiert. 2 Punkte
INSERT INTO artist_contract
(artist_id, label_name, starts_on, ends_on, royalty_rate)
VALUES
(7, 'Blue Note', DATE '2024-01-01', NULL, 0.18);
INSERT INTO artist_contract
(artist_id, label_name, starts_on, ends_on, royalty_rate)
VALUES
(999, 'Ghost Label', DATE '2024-01-01', NULL, 0.18);
INSERT INTO artist_contract
(artist_id, label_name, starts_on, ends_on, royalty_rate)
VALUES
(7, 'Blue Note', DATE '2024-01-01', DATE '2023-12-31', 0.60);
10 PunkteAufgabe 2: Ergebnis einer SQL-Abfrage ermitteln
Gegeben sind die folgenden Tabelleninhalte. Ermitteln Sie das exakte Ergebnis der Abfrage inklusive Spaltennamen und Sortierung.
| customer |
| customer_id | email | country |
| 1 | anna@example.org | DE |
| 2 | bo@example.org | DE |
| 3 | carla@example.org | FR |
| 4 | dan@example.org | NULL |
| invoice |
| invoice_id | customer_id | status | total | invoice_date |
| 10 | 1 | PAID | 20.00 | 2025-01-05 |
| 11 | 1 | OPEN | 15.00 | 2025-01-06 |
| 12 | 2 | PAID | NULL | 2025-02-01 |
| 13 | 3 | PAID | 35.00 | 2025-02-03 |
| 14 | 3 | CANCELLED | 40.00 | 2025-02-04 |
SELECT
COALESCE(c.country, '?') AS land,
COUNT(i.invoice_id) AS rechnungen,
COUNT(*) AS join_zeilen,
SUM(
CASE
WHEN i.status = 'PAID' THEN COALESCE(i.total, 0)
ELSE 0
END
) AS paid_total
FROM customer c
LEFT JOIN invoice i
ON i.customer_id = c.customer_id
AND i.status <> 'CANCELLED'
GROUP BY COALESCE(c.country, '?')
HAVING COUNT(i.invoice_id) >= 1
OR COALESCE(c.country, '?') = '?'
ORDER BY paid_total DESC, land ASC;
Erklären Sie zusätzlich in einem Satz, warum die Position des Filters i.status <> 'CANCELLED' hier wichtig ist.
18 PunkteAufgabe 3: SQL-Abfragen schreiben
Formulieren Sie jeweils eine SQL-Abfrage. Verwenden Sie sprechende Aliase.
- Ermitteln Sie alle Alben, deren Tracks zu mindestens zwei unterschiedlichen Genres gehören und deren Gesamtspielzeit länger ist als die durchschnittliche Gesamtspielzeit aller Alben. Auszugeben sind Künstlername, Albumtitel, Anzahl Genres und Gesamtminuten. 5 Punkte
- Ermitteln Sie alle Kunden, für die gilt: Jede bezahlte Rechnung des Kunden hat mindestens zwei Rechnungspositionen. Kunden ohne bezahlte Rechnung sollen nicht erscheinen. 5 Punkte
- Ermitteln Sie pro Genre die Anzahl der Tracks, die noch nie verkauft wurden, aber in mindestens einer Playlist enthalten sind. Genres ohne solche Tracks sollen mit
0 erscheinen. 4 Punkte
- Ermitteln Sie die fünf umsatzstärksten Alben des Jahres 2025. Es zählen nur bezahlte Rechnungen. Auszugeben sind Künstler, Album, Umsatz und Anzahl verkaufter Tracks. 4 Punkte
8 PunkteAufgabe 4: Views, Index, Rechte und Systemkatalog
- Erstellen Sie eine View
v_paid_album_revenue, die je Künstler und Album den Umsatz bezahlter Rechnungen aggregiert. 3 Punkte
- Ist diese View in PostgreSQL direkt änderbar? Begründen Sie kurz. 2 Punkte
- Schlagen Sie zwei sinnvolle Indizes für häufige Auswertungen nach
invoice.status, invoice.invoice_date und Join auf invoice_line.track_id vor. Begründen Sie jeweils knapp. 2 Punkte
- Schreiben Sie eine Abfrage gegen
information_schema, die die Spalten der Tabelle invoice_line mit Datentypen ausgibt. 1 Punkt
16 PunkteAufgabe 5: Function und Trigger in PL/pgSQL
Die Spalte invoice.total soll automatisch aus den Positionen in invoice_line gepflegt werden. Außerdem dürfen Positionen einer bereits bezahlten Rechnung nicht mehr geändert oder gelöscht werden.
- Schreiben Sie eine Function
recalc_invoice_total(p_invoice_id integer), die den Rechnungsbetrag neu berechnet, in invoice.total schreibt und den neuen Betrag zurückgibt. 5 Punkte
- Schreiben Sie eine Trigger-Function, die bei
INSERT, UPDATE und DELETE auf invoice_line die betroffenen Rechnungen neu berechnet. Bei UPDATE ist auch der Fall zu behandeln, dass sich invoice_id ändert. 7 Punkte
- Legen Sie den Trigger an. 2 Punkte
- Nennen Sie eine Nebenläufigkeitsgrenze dieser Lösung und wie man sie entschärfen kann. 2 Punkte
12 PunkteAufgabe 6: Transaktionen, Isolation und Commit-Protokolle
Betrachten Sie die folgende Situation. Ein Trigger aus Aufgabe 5 aktualisiert invoice.total nach jeder Änderung an invoice_line.
-- T1
BEGIN;
SELECT total FROM invoice WHERE invoice_id = 42; -- Ergebnis: 100.00
-- T2
BEGIN;
INSERT INTO invoice_line(invoice_id, line_no, track_id, quantity, unit_price)
VALUES (42, 5, 77, 1, 25.00);
COMMIT;
-- T1
SELECT total FROM invoice WHERE invoice_id = 42;
COMMIT;
- Welches Ergebnis kann die zweite Abfrage von T1 bei
READ COMMITTED liefern, welches bei REPEATABLE READ? Benennen Sie die Anomalie bzw. den Isolationseffekt. 4 Punkte
- Wie kann T1 die Rechnung so lesen, dass parallele Änderungen blockiert werden, solange T1 arbeitet? 2 Punkte
- Skizzieren Sie einen Deadlock mit zwei Transaktionen, die zwei Rechnungen in unterschiedlicher Reihenfolge sperren. Wie vermeidet man ihn praktisch? 3 Punkte
- Grenzen Sie 2PL und 2PC ab. Warum lösen sie unterschiedliche Probleme? 3 Punkte
12 PunkteAufgabe 7: JDBC und JPA
Ein Java-Programm soll alle Tracks eines Genres um einen Faktor verteuern und die Aktion protokollieren. Ergänzen Sie den Code konzeptionell sauber. Es genügt eine kompakte, syntaktisch plausible Lösung.
Connection conn = DriverManager.getConnection(url, props);
String genre = args[0];
BigDecimal factor = new BigDecimal(args[1]);
// START
// UPDATE track ...
// INSERT INTO price_log(genre_name, factor, changed_at) ...
// ENDE
- Schreiben Sie den JDBC-Code zwischen
START und ENDE mit PreparedStatement, expliziter Transaktion, Commit/Rollback und Ressourcenfreigabe. 7 Punkte
- Nennen Sie zwei konkrete Fehler, die bei einer Lösung mit
Statement und String-Konkatenation entstehen können. 2 Punkte
- Ordnen Sie in JPA die Begriffe transient, managed, detached und removed einem Entity-Lebenszyklus zu. 3 Punkte
12 PunkteAufgabe 8: Verteilte Datenbanken und NoSQL
Die Musikplattform wird erweitert. Es entstehen folgende Anforderungen:
- Sitzungs- und Feature-Flag-Daten sollen extrem schnell gelesen und mit kurzer Lebensdauer gespeichert werden.
- Künstlerprofile enthalten je nach Künstler sehr unterschiedliche verschachtelte Metadaten.
- Hörereignisse werden millionenfach pro Tag geschrieben und meist nach
user_id und Tag gelesen.
- Empfehlungen sollen Beziehungsmuster zwischen Künstlern, Playlists und Kunden auswerten.
- Bezahlte Rechnungen bleiben ACID-kritisch.
- Ordnen Sie Redis, MongoDB, Cassandra, Neo4j und das relationale System jeweils passend zu. Begründen Sie die wichtigste Entscheidung pro System. 5 Punkte
- Schreiben Sie eine MongoDB-Abfrage, die aktive Künstlerprofile mit Tag
live findet und nur name und metadata.tourYear ausgibt. 2 Punkte
- Entwerfen Sie eine Cassandra-Tabelle für Hörereignisse pro Nutzer und Tag. Markieren Sie Partition Key und Clustering Columns und nennen Sie eine nicht unterstützte Abfrageart. 3 Punkte
- Schreiben Sie eine Cypher-Abfrage, die Kunden findet, die eine Playlist mit einem Künstler teilen, dessen Name
Metallica ist. 2 Punkte
8 PunkteAufgabe 9: Multiple Choice
Kreuzen Sie für jede Aussage an, ob sie richtig oder falsch ist. Jede Zeile zählt 1 Punkt.
| Nr. |
Aussage |
Richtig |
Falsch |
| 1 | Ein Primärschlüssel erzeugt in PostgreSQL eine Eindeutigkeitsabsicherung, typischerweise über einen Index. | | |
| 2 | Aggregatbedingungen wie COUNT(*) > 1 gehören in die WHERE-Klausel, weil WHERE vor GROUP BY ausgeführt wird. | | |
| 3 | Ein Filter auf der rechten Tabelle eines LEFT JOIN kann in der WHERE-Klausel den OUTER JOIN faktisch zum INNER JOIN machen. | | |
| 4 | Eine PostgreSQL-Function ist der richtige Ort, um beliebig COMMIT und ROLLBACK auszuführen. | | |
| 5 | PreparedStatement schützt auch dann vollständig vor SQL Injection, wenn Tabellen- oder Spaltennamen ungeprüft aus Benutzereingaben konkateniert werden. | | |
| 6 | REPEATABLE READ arbeitet in PostgreSQL mit einem stabilen Snapshot, ist aber nicht identisch mit allgemeiner Serialisierbarkeit. | | |
| 7 | Cassandra eignet sich besonders gut für beliebige Ad-hoc-Joins über mehrere Tabellen. | | |
| 8 | BASE akzeptiert im Vergleich zu strengem ACID eher temporäre Inkonsistenzen, um Verfügbarkeit und Skalierbarkeit zu begünstigen. | | |
Musterlösung
Die folgenden Lösungen zeigen jeweils eine mögliche robuste Lösung. Gleichwertige Varianten sind korrekt, wenn sie die fachlichen Bedingungen einhalten.
Lösung 1: DDL und Integrität
CREATE TABLE artist_contract (
contract_id integer GENERATED ALWAYS AS IDENTITY,
artist_id integer NOT NULL,
label_name varchar(80) NOT NULL,
starts_on date NOT NULL,
ends_on date,
royalty_rate numeric(5,4) NOT NULL,
exclusive boolean NOT NULL DEFAULT true,
CONSTRAINT pk_artist_contract
PRIMARY KEY (contract_id),
CONSTRAINT fk_artist_contract_artist
FOREIGN KEY (artist_id)
REFERENCES artist (artist_id)
ON DELETE CASCADE,
CONSTRAINT ck_artist_contract_dates
CHECK (ends_on IS NULL OR ends_on >= starts_on),
CONSTRAINT ck_artist_contract_royalty
CHECK (royalty_rate > 0 AND royalty_rate <= 0.5),
CONSTRAINT uq_artist_contract_label_start
UNIQUE (artist_id, label_name, starts_on)
);
ALTER TABLE album
ADD CONSTRAINT uq_album_artist_title
UNIQUE (artist_id, title);
Risiko: Der Befehl scheitert, wenn Bestandsdaten bereits doppelte Kombinationen aus artist_id und title enthalten. Außerdem kann das Sperren der Tabelle im Produktivbetrieb stören.
| Insert | Bewertung |
| 1 | Zulässig, wenn noch kein gleicher Vertrag existiert. exclusive erhält den Default true. |
| 2 | Nicht zulässig, weil der Fremdschlüssel auf artist_id = 999 verletzt wird. |
| 3 | Nicht zulässig: ends_on liegt vor starts_on und royalty_rate = 0.60 verletzt den CHECK. |
Lösung 2: Abfrageergebnis
| land | rechnungen | join_zeilen | paid_total |
| FR | 1 | 1 | 35.00 |
| DE | 3 | 3 | 20.00 |
| ? | 0 | 1 | 0 |
Der Filter auf CANCELLED steht in der ON-Bedingung, deshalb bleiben Kunden ohne passende Rechnung beim LEFT JOIN erhalten; in der WHERE-Klausel würden die NULL-erweiterten Zeilen herausfallen.
Lösung 3: SQL-Abfragen
-- 1) Alben mit mindestens zwei Genres und überdurchschnittlicher Gesamtspielzeit
WITH album_lengths AS (
SELECT
a.album_id,
ar.name AS artist_name,
a.title AS album_title,
COUNT(DISTINCT t.genre_id) AS genre_count,
SUM(t.milliseconds) AS total_ms
FROM album a
JOIN artist ar ON ar.artist_id = a.artist_id
JOIN track t ON t.album_id = a.album_id
GROUP BY a.album_id, ar.name, a.title
)
SELECT
artist_name,
album_title,
genre_count,
ROUND(total_ms / 60000.0, 2) AS total_minutes
FROM album_lengths
WHERE genre_count >= 2
AND total_ms > (SELECT AVG(total_ms) FROM album_lengths)
ORDER BY total_minutes DESC;
-- 2) Kunden, deren jede bezahlte Rechnung mindestens zwei Positionen hat
SELECT c.customer_id, c.email
FROM customer c
WHERE EXISTS (
SELECT 1
FROM invoice i
WHERE i.customer_id = c.customer_id
AND i.status = 'PAID'
)
AND NOT EXISTS (
SELECT 1
FROM invoice i
WHERE i.customer_id = c.customer_id
AND i.status = 'PAID'
AND (
SELECT COUNT(*)
FROM invoice_line il
WHERE il.invoice_id = i.invoice_id
) < 2
);
-- 3) Pro Genre: nie verkaufte, aber in Playlist enthaltene Tracks
SELECT
g.name AS genre_name,
COUNT(t.track_id) AS unsold_playlist_tracks
FROM genre g
LEFT JOIN track t
ON t.genre_id = g.genre_id
AND EXISTS (
SELECT 1
FROM playlist_track pt
WHERE pt.track_id = t.track_id
)
AND NOT EXISTS (
SELECT 1
FROM invoice_line il
WHERE il.track_id = t.track_id
)
GROUP BY g.genre_id, g.name
ORDER BY g.name;
-- 4) Top-5-Alben nach Umsatz im Jahr 2025
SELECT
ar.name AS artist_name,
a.title AS album_title,
SUM(il.quantity * il.unit_price) AS revenue,
SUM(il.quantity) AS sold_tracks
FROM invoice i
JOIN invoice_line il ON il.invoice_id = i.invoice_id
JOIN track t ON t.track_id = il.track_id
JOIN album a ON a.album_id = t.album_id
JOIN artist ar ON ar.artist_id = a.artist_id
WHERE i.status = 'PAID'
AND i.invoice_date >= DATE '2025-01-01'
AND i.invoice_date < DATE '2026-01-01'
GROUP BY ar.artist_id, ar.name, a.album_id, a.title
ORDER BY revenue DESC, sold_tracks DESC, artist_name, album_title
LIMIT 5;
Lösung 4: Views, Index, Rechte und Systemkatalog
CREATE OR REPLACE VIEW v_paid_album_revenue AS
SELECT
ar.artist_id,
ar.name AS artist_name,
a.album_id,
a.title AS album_title,
SUM(il.quantity * il.unit_price) AS revenue
FROM invoice i
JOIN invoice_line il ON il.invoice_id = i.invoice_id
JOIN track t ON t.track_id = il.track_id
JOIN album a ON a.album_id = t.album_id
JOIN artist ar ON ar.artist_id = a.artist_id
WHERE i.status = 'PAID'
GROUP BY ar.artist_id, ar.name, a.album_id, a.title;
Die View ist nicht direkt änderbar, weil sie Joins, Aggregation und GROUP BY enthält. Änderungen müssten über Basistabellen oder über einen passenden INSTEAD OF-Trigger laufen.
CREATE INDEX ix_invoice_status_date
ON invoice (status, invoice_date);
CREATE INDEX ix_invoice_line_track
ON invoice_line (track_id);
Der erste Index unterstützt Filter nach Status und Zeitraum, der zweite Index unterstützt Joins und Existenzprüfungen von Track zu Rechnungspositionen.
SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'invoice_line'
ORDER BY ordinal_position;
Lösung 5: Function und Trigger
CREATE OR REPLACE FUNCTION recalc_invoice_total(p_invoice_id integer)
RETURNS numeric
LANGUAGE plpgsql
AS $$
DECLARE
v_total numeric(10,2);
BEGIN
SELECT COALESCE(SUM(quantity * unit_price), 0)
INTO v_total
FROM invoice_line
WHERE invoice_id = p_invoice_id;
UPDATE invoice
SET total = v_total
WHERE invoice_id = p_invoice_id;
RETURN v_total;
END;
$$;
CREATE OR REPLACE FUNCTION trg_invoice_line_recalc()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
IF TG_OP IN ('UPDATE', 'DELETE') AND EXISTS (
SELECT 1
FROM invoice
WHERE invoice_id = OLD.invoice_id
AND status = 'PAID'
) THEN
RAISE EXCEPTION 'Paid invoice % must not be changed', OLD.invoice_id;
END IF;
IF TG_OP IN ('INSERT', 'UPDATE') THEN
IF NEW.quantity <= 0 OR NEW.unit_price < 0 THEN
RAISE EXCEPTION 'Invalid invoice line values';
END IF;
IF EXISTS (
SELECT 1
FROM invoice
WHERE invoice_id = NEW.invoice_id
AND status = 'PAID'
) THEN
RAISE EXCEPTION 'Paid invoice % must not be changed', NEW.invoice_id;
END IF;
END IF;
IF TG_OP IN ('UPDATE', 'DELETE') THEN
PERFORM recalc_invoice_total(OLD.invoice_id);
END IF;
IF TG_OP IN ('INSERT', 'UPDATE') THEN
PERFORM recalc_invoice_total(NEW.invoice_id);
RETURN NEW;
END IF;
RETURN OLD;
END;
$$;
CREATE TRIGGER invoice_line_recalc_after_change
AFTER INSERT OR UPDATE OR DELETE ON invoice_line
FOR EACH ROW
EXECUTE FUNCTION trg_invoice_line_recalc();
Nebenläufigkeitsgrenze: Zwei parallele Änderungen derselben Rechnung können sich gegenseitig überholen oder unerwartet blockieren. Praktisch entschärft man das durch klare Transaktionsgrenzen und Sperren der Rechnungszeile, z. B. SELECT ... FOR UPDATE auf invoice, bevor Positionen geändert werden.
Lösung 6: Transaktionen
Bei READ COMMITTED kann T1 beim zweiten Lesen 125.00 sehen, weil T2 inzwischen committet hat und der Trigger invoice.total aktualisiert hat. Das ist ein Non-Repeatable Read auf der Rechnungszeile; liest T1 direkt die Positionen per Aggregat, entspricht der neu auftauchende Datensatz zusätzlich einem Phantom-Effekt.
Bei REPEATABLE READ arbeitet T1 mit einem stabilen Snapshot und sieht beim zweiten Lesen weiterhin 100.00, solange T1 nur liest. Für blockierendes Lesen:
BEGIN;
SELECT total
FROM invoice
WHERE invoice_id = 42
FOR UPDATE;
Deadlock-Beispiel: T1 sperrt Rechnung 1 und wartet danach auf Rechnung 2; T2 sperrt Rechnung 2 und wartet danach auf Rechnung 1. Vermeidung: alle Transaktionen sperren Ressourcen in derselben Reihenfolge, halten Sperren kurz und behandeln Deadlock-Fehler mit Retry.
2PL ist ein Sperrprotokoll für Isolation: Sperren werden in einer Wachstumsphase erworben und in einer Schrumpfungsphase freigegeben. 2PC ist ein Commit-Protokoll für verteilte Transaktionen: Coordinator fragt Teilnehmer in der Prepare-Phase und entscheidet danach Commit oder Rollback. 2PL verhindert Nebenläufigkeitsanomalien, 2PC koordiniert atomaren Abschluss über mehrere Systeme.
Lösung 7: JDBC und JPA
String updateSql = """
UPDATE track t
SET unit_price = unit_price * ?
FROM genre g
WHERE g.genre_id = t.genre_id
AND g.name = ?
""";
String logSql = """
INSERT INTO price_log(genre_name, factor, changed_at)
VALUES (?, ?, CURRENT_TIMESTAMP)
""";
try {
conn.setAutoCommit(false);
try (PreparedStatement update = conn.prepareStatement(updateSql);
PreparedStatement log = conn.prepareStatement(logSql)) {
update.setBigDecimal(1, factor);
update.setString(2, genre);
int changedRows = update.executeUpdate();
log.setString(1, genre);
log.setBigDecimal(2, factor);
log.executeUpdate();
if (changedRows == 0) {
throw new SQLException("No tracks changed for genre: " + genre);
}
}
conn.commit();
} catch (SQLException ex) {
conn.rollback();
throw ex;
} finally {
conn.setAutoCommit(true);
conn.close();
}
Fehler bei Statement und String-Konkatenation: SQL Injection über genre und fehlerhafte Typ-/Formatbehandlung bei Zahlen oder Strings. Außerdem gehen Ausführungsplan-Wiederverwendung und klare Parameterbindung verloren.
| JPA-Zustand | Bedeutung |
| transient | Objekt existiert nur in Java, ist noch nicht mit dem Persistence Context verbunden. |
| managed | Objekt wird vom EntityManager verwaltet; Änderungen werden innerhalb der Transaktion synchronisiert. |
| detached | Objekt war persistent, ist aber nicht mehr an einen aktiven Persistence Context gebunden. |
| removed | Objekt ist zum Löschen markiert und wird beim Flush/Commit aus der Datenbank entfernt. |
Lösung 8: Verteilte Datenbanken und NoSQL
| Anforderung | Passendes System | Begründung |
| Sitzungen und Feature Flags | Redis | Key-Value-Zugriff, sehr geringe Latenz, TTL und einfache Datenstrukturen. |
| Flexible Künstlerprofile | MongoDB | Dokumentmodell passt zu variierenden verschachtelten Metadaten. |
| Hörereignisse nach Nutzer und Tag | Cassandra | Hoher Write-Durchsatz und Query-First-Modell über Partition Key. |
| Beziehungsmuster | Neo4j | Graphmodell macht Beziehungen und Pfadmuster direkt abfragbar. |
| Bezahlte Rechnungen | Relationales DBMS | ACID, Constraints, Transaktionen und referentielle Integrität sind zentral. |
db.artist_profiles.find(
{ active: true, tags: "live" },
{ _id: 0, name: 1, "metadata.tourYear": 1 }
);
CREATE TABLE listen_events_by_user_day (
user_id uuid,
event_day date,
listened_at timestamp,
track_id uuid,
artist_id uuid,
seconds_played int,
PRIMARY KEY ((user_id, event_day), listened_at, track_id)
) WITH CLUSTERING ORDER BY (listened_at DESC, track_id ASC);
Partition Key: (user_id, event_day). Clustering Columns: listened_at, track_id. Nicht passend ist z. B. eine beliebige Suche nach artist_id über alle Nutzer und Tage ohne eigene Tabelle oder Sekundärmodellierung.
MATCH (c:Customer)-[:USES]->(p:Playlist)-[:CONTAINS]->(:Track)
-[:PERFORMED_BY]->(a:Artist {name: 'Metallica'})
RETURN DISTINCT c.customer_id, c.email
ORDER BY c.email;
Lösung 9: Multiple Choice
| Nr. | Richtig/Falsch | Kurzbegründung |
| 1 | Richtig | Primärschlüssel sichern Eindeutigkeit und werden in PostgreSQL indexgestützt umgesetzt. |
| 2 | Falsch | Aggregatbedingungen gehören nach der Gruppierung in HAVING. |
| 3 | Richtig | Ein WHERE-Filter auf rechte Spalten entfernt NULL-erweiterte Zeilen. |
| 4 | Falsch | Transaktionssteuerung gehört nicht beliebig in Functions; Procedures haben engere Sonderfälle. |
| 5 | Falsch | Parameter schützen Werte, aber keine ungeprüft konkatenierten Objektbezeichner. |
| 6 | Richtig | PostgreSQL nutzt einen stabilen Snapshot; Serialisierbarkeit ist ein stärkerer Level. |
| 7 | Falsch | Cassandra ist query-orientiert und nicht für beliebige Joins gebaut. |
| 8 | Richtig | BASE steht für weichere Konsistenzmodelle und eventual consistency. |
Bewertungsschlüssel
| Bereich | Punkte |
| DDL und Integrität | 14 |
| Abfrageergebnis | 10 |
| SQL-Abfragen schreiben | 18 |
| Views, Index, Rechte, Systemkatalog | 8 |
| Function und Trigger | 16 |
| Transaktionen | 12 |
| JDBC und JPA | 12 |
| NoSQL und verteilte Datenbanken | 12 |
| Multiple Choice | 8 |
| Gesamt | 110 |
Verwendete Quellenbereiche
| Quelle |
Verwendete Schwerpunkte |
| Beispiele Klausuraufgaben.pdf | Aufgabentypen: DDL nach Schema, Abfrageergebnis, Multiple Choice, SQL-Abfragen, Functions/Trigger, JDBC-Code und Wissensfragen zu anderen Datenbankkonzepten. |
| 1 - Einführung | Datenbankbegriffe, DBMS, ANSI-SPARC, Rollen und praktische Aufgabenprofile. |
| 2 - SQL Teil 1 DDL/DML | PostgreSQL, DBeaver, Datentypen, Constraints, referentielle Integrität, INSERT/UPDATE/DELETE. |
| 3 - SQL Teil 2 DQL | SELECT-Reihenfolge, Aggregation, WHERE/HAVING, JOINs, Subqueries, Mengenoperationen und komplexe Abfragen. |
| 4 - SQL Teil 3 Zusatzthemen | Views, Updatefähigkeit, Indizes, Rechteverwaltung und Information Schema. |
| 5 - DB-seitige Programmierung und Transaktionen | SQL-/PL/pgSQL-Functions, Procedures, Trigger, ACID, Isolation, Locks, Deadlocks, 2PL und 2PC. |
| 6 - JDBC und JPA | JDBC-Ablauf, Prepared Statements, Transaktionssteuerung, SQL Injection, REST/3-Schichten, ORM und Entity-Lebenszyklus. |
| 7 - Erweiterungen RDBMS und verteilte DB | OLTP/OLAP, Column Store, Code Pushdown, verteilte Datenbanken, CAP und BASE. |
| 8 - NoSQL Datenbanken | Redis, MongoDB, Cassandra, Neo4j, Polystores, Einsatzszenarien und Konsistenzentscheidungen. |