Aufwärmen · Aufgabe 1 von 7
// B4.3 · ca. 30 Min. · Einstieg
Constraints: Primärschlüssel, Fremdschlüssel, UNIQUE, CHECK
Nach dieser Lektion geben Sie einer Tabelle Regeln, die fehlerhafte Zeilen ablehnen, verknüpfen Ausleihen mit echten Büchern und Mitgliedern, legen fest, was ein Löschen mit ihnen macht, und lesen den Fehler einer verletzten Regel.
Danach können Sie
- NOT NULL, UNIQUE, PRIMARY KEY und CHECK deklarieren und vorhersagen, welche Zeilen sie ablehnen
- Tabellen mit REFERENCES verknüpfen und wählen, was ON DELETE tut: NO ACTION, RESTRICT oder CASCADE
- Einen Constraint-Fehler lesen und die Regel und die Zeile dahinter finden
Vorhersagen · Aufgabe 2 von 7
Sagen Sie es voraus, bevor Sie weiterlesen. Was passiert beim zweiten INSERT?
CREATE TABLE members (id integer PRIMARY KEY, name text); INSERT INTO members VALUES (1, 'Ada'); INSERT INTO members VALUES (1, 'Grace');Üben · Aufgabe 3 von 7
Ergänzen Sie den Operator, damit copies 0 oder mehr sein darf, aber nie negativ.
CREATE TABLE books (title text, copies integer CHECK (copies ____ 0));CREATE TABLE books (title text, copies integer CHECK (copies 0));Üben · Aufgabe 4 von 7
Ordnen Sie jedem Constraint die Zeile zu, die er ablehnt.
Üben · Aufgabe 5 von 7
In der Bibliothek aus dem durchgerechneten Beispiel hat loans.member_id ON DELETE CASCADE. Von den fünf Ausleihen gehören zwei Grace (Mitglied 2). Wie viele Ausleihen bleiben?
DELETE FROM members WHERE id = 2;Denksport · Aufgabe 6 von 7
Knobelaufgabe. loans hat CHECK (returned_on >= loaned_on) und fünf Zeilen. Die neue Ausleihe hat noch kein Rückgabedatum. Was gibt das SELECT zurück?
INSERT INTO loans VALUES (6, 4, 4, '2026-10-03', NULL); SELECT count(*) FROM loans;Anwenden · Aufgabe 7 von 7
Mini-Aufgabe. Erstellen Sie mit der Bibliothek mit Schlüsseln aus dem durchgerechneten Beispiel reviews: eine id, die jede Rezension identifiziert, das Buch und das Mitglied (beide Pflicht, und beide müssen existieren; wird ein Buch oder Mitglied gelöscht, gehen seine Rezensionen mit) und stars von 1 bis 5, Pflicht. Ein Mitglied darf ein Buch nur einmal bewerten. Fügen Sie zwei Rezensionen ein, versuchen Sie dann eine dritte desselben Mitglieds zum selben Buch und stars = 6, und lesen Sie beide Fehler.
Prüfen Sie Ihr Ergebnis anhand dieser Liste
Selbst programmieren
Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.
Ausgearbeitetes Beispiel
Die Bibliothek mit Schlüsseln und Regeln
main.sql baut die Bibliothek aus Modul B1 noch einmal, diesmal mit Regeln: Schlüssel auf jeder id, NOT NULL, wo ein Wert Pflicht ist, eine eindeutige E-Mail, CHECKs auf copies und den Datumswerten und Fremdschlüssel von loans zu books und members. Es listet die Constraints von loans mit den Namen auf, die PostgreSQL ihnen gegeben hat, löscht dann Linus und zählt die Ausleihen. Auf Ihrem Computer: psql -X -q -v ON_ERROR_STOP=1 -f main.sql.
main.sql
-- Referenced tables first: loans refers to both.
CREATE TABLE members (
id integer PRIMARY KEY,
name text NOT NULL,
email text UNIQUE,
city text,
joined date NOT NULL
);
CREATE TABLE books (
id integer PRIMARY KEY,
title text NOT NULL,
author text,
year integer,
genre text,
copies integer NOT NULL CHECK (copies >= 0)
);
INSERT INTO members VALUES
(1, 'Ada', 'ada@example.com', 'Berlin', '2025-01-15'),
(2, 'Grace', 'grace@example.com', 'Munich', '2025-03-02'),
(3, 'Linus', NULL, 'Berlin', '2026-02-10'),
(4, 'Margaret', 'margaret@example.com', NULL, '2026-05-20');
INSERT INTO books VALUES
(1, 'Dune', 'Frank Herbert', 1965, 'sci-fi', 2),
(2, 'Emma', 'Jane Austen', 1815, 'classic', 1),
(3, 'Kindred', 'Octavia E. Butler', 1979, 'sci-fi', 1),
(4, 'Beloved', 'Toni Morrison', 1987, 'fiction', 1),
(5, 'The Hobbit', 'J. R. R. Tolkien', 1937, 'fantasy', 3),
(6, 'Neuromancer', 'William Gibson', 1984, 'sci-fi', 1);
-- Every loan must point to an existing book and member.
CREATE TABLE loans (
id integer PRIMARY KEY,
book_id integer NOT NULL REFERENCES books (id),
member_id integer NOT NULL REFERENCES members (id) ON DELETE CASCADE,
loaned_on date NOT NULL,
returned_on date,
CHECK (returned_on >= loaned_on)
);
INSERT INTO loans VALUES
(1, 1, 1, '2026-09-01', '2026-09-10'),
(2, 3, 1, '2026-09-12', NULL),
(3, 2, 2, '2026-09-05', '2026-09-20'),
(4, 1, 3, '2026-09-15', NULL),
(5, 6, 2, '2026-09-18', NULL);
-- The rules of loans and their names (p primary key, f foreign key, c check, n not null).
SELECT conname, contype
FROM pg_constraint
WHERE conrelid = 'loans'::regclass
ORDER BY conname;
-- ON DELETE CASCADE: Linus leaves, and his loan goes with him.
DELETE FROM members WHERE id = 3;
SELECT count(*) AS loans_left FROM loans;
Ausführen mit
psql -X -q -v ON_ERROR_STOP=1 -f main.sqlAusgabe
conname | contype
--------------------------+---------
loans_book_id_fkey | f
loans_book_id_not_null | n
loans_check | c
loans_id_not_null | n
loans_loaned_on_not_null | n
loans_member_id_fkey | f
loans_member_id_not_null | n
loans_pkey | p
(8 rows)
loans_left
------------
4
(1 row)- Der CHECK über zwei Spalten wurde loans_check genannt; die anderen tragen ihren Spaltennamen.
- Der Primärschlüssel hat id auch NOT NULL gemacht: loans_id_not_null.
- Jede Zeile der Daten hat jede Regel bestanden, also liefen alle INSERTs.
- Das Löschen von Mitglied 3 hat auch Ausleihe 4 gelöscht, die einzige, die auf ihn verwies.
Ändern und ausführen
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser PostgreSQL herunter (bis zu 10.1 MB) und speichert es im Cache. Jeder Lauf beginnt mit einer leeren Datenbank. Ihr Code bleibt auf Ihrem Gerät.
Übungen
Übung 1 von 2
Geben Sie books seine Regeln
Der Startcode legt books nur mit Typen an, also nähme PostgreSQL ein Buch ohne Titel oder mit -5 Exemplaren an. Ergänzen Sie Regeln in CREATE TABLE: id ist der Primärschlüssel, title darf nicht NULL sein, und copies darf nicht NULL sein und muss 0 oder mehr sein (ein CHECK). Behalten Sie die sechs eingefügten Bücher.
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser PostgreSQL herunter (bis zu 10.1 MB) und speichert es im Cache. Jeder Lauf beginnt mit einer leeren Datenbank. Ihr Code bleibt auf Ihrem Gerät.
Hinweise
Hinweis 1
Constraints stehen nach dem Typ ihrer Spalte: id integer PRIMARY KEY.
Hinweis 2
Eine Spalte kann zwei haben: copies integer NOT NULL CHECK (…).
Hinweis 3
Die Bedingung für copies ist copies >= 0, in Klammern nach CHECK.
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
CREATE TABLE books (
id integer PRIMARY KEY,
title text NOT NULL,
author text,
year integer,
genre text,
copies integer NOT NULL CHECK (copies >= 0)
);
INSERT INTO books VALUES
(1, 'Dune', 'Frank Herbert', 1965, 'sci-fi', 2),
(2, 'Emma', 'Jane Austen', 1815, 'classic', 1),
(3, 'Kindred', 'Octavia E. Butler', 1979, 'sci-fi', 1),
(4, 'Beloved', 'Toni Morrison', 1987, 'fiction', 1),
(5, 'The Hobbit', 'J. R. R. Tolkien', 1937, 'fantasy', 3),
(6, 'Neuromancer', 'William Gibson', 1984, 'sci-fi', 1);
Auf dem eigenen Computer ausführen
Installieren Sie PostgreSQL 18 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.sql
-- Types only: PostgreSQL checks nothing else yet.
CREATE TABLE books (
id integer,
title text,
author text,
year integer,
genre text,
copies integer
);
INSERT INTO books VALUES
(1, 'Dune', 'Frank Herbert', 1965, 'sci-fi', 2),
(2, 'Emma', 'Jane Austen', 1815, 'classic', 1),
(3, 'Kindred', 'Octavia E. Butler', 1979, 'sci-fi', 1),
(4, 'Beloved', 'Toni Morrison', 1987, 'fiction', 1),
(5, 'The Hobbit', 'J. R. R. Tolkien', 1937, 'fantasy', 3),
(6, 'Neuromancer', 'William Gibson', 1984, 'sci-fi', 1);
test.sql
-- test: id ist der Primärschlüssel von books
SELECT EXISTS (SELECT 1 FROM pg_constraint c JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = ANY (c.conkey) WHERE c.conrelid = 'books'::regclass AND c.contype = 'p' AND cardinality(c.conkey) = 1 AND a.attname = 'id');
-- test: title ist NOT NULL
SELECT attnotnull FROM pg_attribute WHERE attrelid = 'books'::regclass AND attname = 'title';
-- test: copies ist NOT NULL
SELECT attnotnull FROM pg_attribute WHERE attrelid = 'books'::regclass AND attname = 'copies';
-- test: Ein CHECK-Constraint schützt copies
SELECT EXISTS (SELECT 1 FROM pg_constraint c JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = ANY (c.conkey) WHERE c.conrelid = 'books'::regclass AND c.contype = 'c' AND a.attname = 'copies');
-- test: Die sechs Bücher sind in der Tabelle
SELECT count(*) = 6 FROM books;
psql führt die Dateien auf Ihrem eigenen PostgreSQL-Server aus. Nehmen Sie eine leere Datenbank, die Sie wegwerfen können: Hängen Sie -d und ihren Namen an den Befehl an.
Programm ausführen:
psql -X -q -v ON_ERROR_STOP=1 -f main.sqlFür die Prüfungen gibt es auf Ihrem Computer noch keinen Befehl. Sie stehen in test.sql: Jede Prüfabfrage gibt t aus, wenn sie besteht.
Übung 2 von 2
Ausleihen, die auf echte Zeilen zeigen
setup.sql hat members und books mit ihren Schlüsseln angelegt. Der Startcode legt loans ohne Regeln an, dann verlässt Linus (Mitglied 3) die Bibliothek, und seine Ausleihe bleibt zurück und zeigt auf niemanden. Geben Sie loans seine Regeln: id ist der Primärschlüssel, book_id verweist auf books (id), member_id verweist auf members (id) mit ON DELETE CASCADE, und ein CHECK stellt sicher, dass returned_on nicht vor loaned_on liegt. Dann geht die Ausleihe von Linus mit ihm.
Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.
Beim ersten Ausführen lädt Ihr Browser PostgreSQL herunter (bis zu 10.1 MB) und speichert es im Cache. Jeder Lauf beginnt mit einer leeren Datenbank. Ihr Code bleibt auf Ihrem Gerät.
Hinweise
Hinweis 1
Ein Fremdschlüssel steht nach dem Spaltentyp: book_id integer REFERENCES books (id).
Hinweis 2
ON DELETE CASCADE folgt auf den REFERENCES-Teil von member_id.
Hinweis 3
Ein CHECK über zwei Spalten steht nach der letzten Spalte: , CHECK (returned_on >= loaned_on)
Eine Lösung zeigen
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
CREATE TABLE loans (
id integer PRIMARY KEY,
book_id integer REFERENCES books (id),
member_id integer REFERENCES members (id) ON DELETE CASCADE,
loaned_on date,
returned_on date,
CHECK (returned_on >= loaned_on)
);
INSERT INTO loans VALUES
(1, 1, 1, '2026-09-01', '2026-09-10'),
(2, 3, 1, '2026-09-12', NULL),
(3, 2, 2, '2026-09-05', '2026-09-20'),
(4, 1, 3, '2026-09-15', NULL),
(5, 6, 2, '2026-09-18', NULL);
DELETE FROM members WHERE id = 3;
SELECT id, book_id, member_id FROM loans ORDER BY id;
Auf dem eigenen Computer ausführen
Installieren Sie PostgreSQL 18 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.sql
-- loans without rules, then Linus leaves the library.
CREATE TABLE loans (
id integer,
book_id integer,
member_id integer,
loaned_on date,
returned_on date
);
INSERT INTO loans VALUES
(1, 1, 1, '2026-09-01', '2026-09-10'),
(2, 3, 1, '2026-09-12', NULL),
(3, 2, 2, '2026-09-05', '2026-09-20'),
(4, 1, 3, '2026-09-15', NULL),
(5, 6, 2, '2026-09-18', NULL);
DELETE FROM members WHERE id = 3;
SELECT id, book_id, member_id FROM loans ORDER BY id;
test.sql
-- test: id ist der Primärschlüssel von loans
SELECT EXISTS (SELECT 1 FROM pg_constraint c JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = ANY (c.conkey) WHERE c.conrelid = 'loans'::regclass AND c.contype = 'p' AND cardinality(c.conkey) = 1 AND a.attname = 'id');
-- test: book_id verweist auf books
SELECT EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'loans'::regclass AND contype = 'f' AND confrelid = 'books'::regclass);
-- test: member_id verweist auf members, mit ON DELETE CASCADE
SELECT EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'loans'::regclass AND contype = 'f' AND confrelid = 'members'::regclass AND confdeltype = 'c');
-- test: Ein CHECK-Constraint vergleicht die Datumswerte
SELECT EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'loans'::regclass AND contype = 'c');
-- test: Die Ausleihe von Linus ist mit ihm weg, die anderen vier bleiben
-- output:
-- id | book_id | member_id
-- ----+---------+-----------
-- 1 | 1 | 1
-- 2 | 3 | 1
-- 3 | 2 | 2
-- 5 | 6 | 2
-- (4 rows)
setup.sql
CREATE TABLE members (
id integer PRIMARY KEY,
name text NOT NULL,
email text UNIQUE,
city text,
joined date NOT NULL
);
CREATE TABLE books (
id integer PRIMARY KEY,
title text NOT NULL,
author text,
year integer,
genre text,
copies integer NOT NULL CHECK (copies >= 0)
);
INSERT INTO members VALUES
(1, 'Ada', 'ada@example.com', 'Berlin', '2025-01-15'),
(2, 'Grace', 'grace@example.com', 'Munich', '2025-03-02'),
(3, 'Linus', NULL, 'Berlin', '2026-02-10'),
(4, 'Margaret', 'margaret@example.com', NULL, '2026-05-20');
INSERT INTO books VALUES
(1, 'Dune', 'Frank Herbert', 1965, 'sci-fi', 2),
(2, 'Emma', 'Jane Austen', 1815, 'classic', 1),
(3, 'Kindred', 'Octavia E. Butler', 1979, 'sci-fi', 1),
(4, 'Beloved', 'Toni Morrison', 1987, 'fiction', 1),
(5, 'The Hobbit', 'J. R. R. Tolkien', 1937, 'fantasy', 3),
(6, 'Neuromancer', 'William Gibson', 1984, 'sci-fi', 1);
psql führt die Dateien auf Ihrem eigenen PostgreSQL-Server aus. Nehmen Sie eine leere Datenbank, die Sie wegwerfen können: Hängen Sie -d und ihren Namen an den Befehl an.
Programm ausführen:
psql -X -q -v ON_ERROR_STOP=1 -f setup.sql -f main.sqlFür die Prüfungen gibt es auf Ihrem Computer noch keinen Befehl. Sie stehen in test.sql: Jede Prüfabfrage gibt t aus, wenn sie besteht.
Häufige Fehler
Eine zweite Zeile mit demselben Schlüssel
INSERT INTO books VALUES (1, 'Dune', 'Frank Herbert', 1965, 'sci-fi', 2);
Was psql ausgibt
ERROR: duplicate key value violates unique constraint "books_pkey"Warum, und die Lösung
books hat schon eine Zeile mit id 1, und der Primärschlüssel erlaubt jede id nur einmal; die DETAIL-Zeile nennt den Schlüssel, (id)=(1). Die übliche Ursache ist ein Datenskript, das zweimal läuft. Geben Sie der neuen Zeile eine id, die keine andere Zeile hat, oder lassen Sie das INSERT weg, wenn die Zeile schon da ist, bzw. ändern Sie sie mit UPDATE.
Eine Ausleihe für ein Mitglied, das es nicht gibt
INSERT INTO loans VALUES (6, 1, 9, '2026-10-03', NULL);
Was psql ausgibt
ERROR: insert or update on table "loans" violates foreign key constraint "loans_member_id_fkey"Warum, und die Lösung
member_id REFERENCES members (id), und kein Mitglied hat die id 9 (DETAIL: Key (member_id)=(9) is not present in table "members"). Legen Sie zuerst das Mitglied an, dann die Ausleihe, oder verwenden Sie die id eines vorhandenen Mitglieds. Aus demselben Grund zählt die Reihenfolge beim Laden von Daten: Eltern vor Kindern.
Ein Buch löschen, das noch verliehen ist
DELETE FROM books WHERE id = 1;
Was psql ausgibt
ERROR: update or delete on table "books" violates foreign key constraint "loans_book_id_fkey" on table "loans"Warum, und die Lösung
Zwei Ausleihen verweisen noch auf Dune, und book_id hat die Standardaktion NO ACTION, also wird das Löschen abgelehnt. Löschen oder ändern Sie zuerst diese Ausleihen, oder deklarieren Sie REFERENCES books (id) ON DELETE CASCADE, wenn die Ausleihen mit dem Buch verschwinden sollen. RESTRICT lehnte es ebenfalls ab, mit den Worten violates RESTRICT setting of foreign key constraint.
PostgreSQL im Browser: PGlite 0.5.8 (PostgreSQL 18.3), Apache-2.0 und PostgreSQL License. Lizenz und Quellcode
Abschlussquiz
5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.
Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.