Aufwärmen · Aufgabe 1 von 7
Aufwärmen: Die Tabelle books hat sechs Zeilen. Was liefert diese Abfrage?
SELECT count(*) FROM books;// B3.4 · ca. 30 Min. · Einstieg
Nach dieser Lektion beantworten Sie „wie viele“ und „wie viel“ in einer Abfrage: für eine ganze Tabelle oder je Genre, Mitglied oder Stadt.
Danach können Sie
Aufwärmen · Aufgabe 1 von 7
SELECT count(*) FROM books;Vorhersagen · Aufgabe 2 von 7
SELECT genre, count(*)
FROM books
GROUP BY genre;Üben · Aufgabe 3 von 7
SELECT count(____ member_id) FROM loans;Üben · Aufgabe 4 von 7
Üben · Aufgabe 5 von 7
Denksport · Aufgabe 6 von 7
SELECT count(*), sum(copies)
FROM books
WHERE year > 2000;Anwenden · Aufgabe 7 von 7
Prüfen Sie Ihr Ergebnis anhand dieser Liste
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
setup.sql lädt die Bibliothek aus Modul B1: Bücher, Mitglieder und Ausleihen. main.sql fasst den Katalog in einer Zeile zusammen, zählt die Ausleihen auf drei Arten, gruppiert dann die Bücher nach Genre und, nach einem Join, die Ausleihen nach Genre. Auf Ihrem Computer: psql -X -q -v ON_ERROR_STOP=1 -f setup.sql -f main.sql.
main.sql
-- 1. The whole catalogue in one row.
SELECT count(*) AS books,
sum(copies) AS copies,
round(avg(copies), 2) AS avg_copies,
min(year) AS oldest,
max(year) AS newest
FROM books;
-- 2. Three ways to count the loans.
SELECT count(*) AS loans,
count(returned_on) AS returned,
count(DISTINCT member_id) AS borrowers
FROM loans;
-- 3. One row per genre, most copies first.
SELECT genre, count(*) AS books, sum(copies) AS copies
FROM books
GROUP BY genre
ORDER BY copies DESC, genre;
-- 4. Loans per genre: join first, then group.
SELECT b.genre, count(*) AS loans, count(DISTINCT l.book_id) AS titles
FROM loans l
JOIN books b ON b.id = l.book_id
GROUP BY b.genre
ORDER BY loans DESC;
setup.sql
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);
CREATE TABLE members (
id integer,
name text,
email text,
city text,
joined date
);
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');
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);
Ausführen mit
psql -X -q -v ON_ERROR_STOP=1 -f setup.sql -f main.sqlAusgabe
books | copies | avg_copies | oldest | newest
-------+--------+------------+--------+--------
6 | 9 | 1.50 | 1815 | 1987
(1 row)
loans | returned | borrowers
-------+----------+-----------
5 | 2 | 3
(1 row)
genre | books | copies
---------+-------+--------
sci-fi | 3 | 4
fantasy | 1 | 3
classic | 1 | 1
fiction | 1 | 1
(4 rows)
genre | loans | titles
---------+-------+--------
sci-fi | 4 | 3
classic | 1 | 1
(2 rows)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.
Übung 1 von 2
Schreiben Sie zwei Abfragen. Erstens: für jedes Genre das Genre, die Zahl der Bücher (books), die Exemplare insgesamt (copies) und das Jahr des ältesten Buchs (oldest), nach Genre sortiert. Zweitens: für jede Stadt der Mitglieder die Stadt, die Zahl der Mitglieder (members) und das späteste Beitrittsdatum (last_joined), nach Stadt sortiert; die Mitglieder ohne Stadt bilden eine eigene Gruppe.
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.
GROUP BY genre liefert eine Zeile je Genre; count(*), sum(copies) und min(year) werden dann je Genre berechnet.
Benennen Sie die Spalten mit AS: count(*) AS books, sum(copies) AS copies, min(year) AS oldest.
Die zweite Abfrage hat dieselbe Form: GROUP BY city, count(*) AS members, max(joined) AS last_joined, ORDER BY city. Die NULL-Stadt kommt zuletzt.
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
SELECT genre, count(*) AS books, sum(copies) AS copies, min(year) AS oldest
FROM books
GROUP BY genre
ORDER BY genre;
SELECT city, count(*) AS members, max(joined) AS last_joined
FROM members
GROUP BY city
ORDER BY city;
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
-- setup.sql has loaded books, members and loans.
SELECT genre, title, year, copies FROM books ORDER BY genre;
test.sql
-- test: Eine Zeile je Genre mit Büchern, Exemplaren und ältestem Jahr, dann eine Zeile je Stadt, die unbekannte Stadt zuletzt
-- output:
-- genre | books | copies | oldest
-- ---------+-------+--------+--------
-- classic | 1 | 1 | 1815
-- fantasy | 1 | 3 | 1937
-- fiction | 1 | 1 | 1987
-- sci-fi | 3 | 4 | 1965
-- (4 rows)
--
-- city | members | last_joined
-- --------+---------+-------------
-- Berlin | 2 | 2026-02-10
-- Munich | 1 | 2025-03-02
-- | 1 | 2026-05-20
-- (3 rows)
setup.sql
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);
CREATE TABLE members (
id integer,
name text,
email text,
city text,
joined date
);
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');
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);
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.
Übung 2 von 2
Die Vorlage zählt die Ausleihen je member_id. Machen Sie daraus einen Bericht, den eine Bibliothekarin lesen kann: Verbinden Sie members, um den Namen zu zeigen, und ergänzen Sie die Zahl der noch offenen Ausleihen (open) und das Datum der letzten Ausleihe (last_loan). Sortieren Sie nach Namen. Eine offene Ausleihe hat kein returned_on.
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.
JOIN members m ON m.id = l.member_id, dann nach Mitglied gruppieren: GROUP BY m.id, m.name.
m.name steht in der SELECT-Liste, also muss es auch in GROUP BY stehen. m.id hält zwei Mitglieder mit gleichem Namen auseinander.
count(*) zählt alle Ausleihen, count(l.returned_on) nur die zurückgegebenen; die Differenz sind die offenen. max(l.loaned_on) ist die letzte Ausleihe.
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
SELECT m.name,
count(*) AS loans,
count(*) - count(l.returned_on) AS open,
max(l.loaned_on) AS last_loan
FROM loans l
JOIN members m ON m.id = l.member_id
GROUP BY m.id, m.name
ORDER BY m.name;
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 per member: only ids so far.
SELECT member_id, count(*) AS loans
FROM loans
GROUP BY member_id
ORDER BY member_id;
test.sql
-- test: Ada, Grace und Linus, jeweils mit Ausleihen, offenen Ausleihen und dem Datum der letzten Ausleihe
-- output:
-- name | loans | open | last_loan
-- -------+-------+------+------------
-- Ada | 2 | 1 | 2026-09-12
-- Grace | 2 | 1 | 2026-09-18
-- Linus | 1 | 1 | 2026-09-15
-- (3 rows)
setup.sql
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);
CREATE TABLE members (
id integer,
name text,
email text,
city text,
joined date
);
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');
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);
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.
SELECT genre, title, count(*)
FROM books
GROUP BY genre;
Was psql ausgibt
ERROR: column "books.title" must appear in the GROUP BY clause or be used in an aggregate functionWarum, und die Lösung
Die Gruppe sci-fi enthält drei Titel, und ihre eine Ergebniszeile hat Platz für einen Wert. PostgreSQL wählt keinen für Sie aus. Gruppieren Sie entweder auch nach title (dann ist jedes Buch eine eigene Gruppe, und alle Zählwerte sind 1), oder setzen Sie title in ein Aggregat: min(title) für einen davon, count(title) für ihre Anzahl.
SELECT genre, sum(title)
FROM books
GROUP BY genre;
Was psql ausgibt
ERROR: function sum(text) does not existWarum, und die Lösung
sum und avg gibt es nur für Zahlentypen (und Intervalle), also keine Summe einer Textspalte. Um die Titel zu zählen, schreiben Sie count(title); für den alphabetisch ersten Titel min(title). Eine Typumwandlung hilft hier nicht: Die Titel sind keine Zahlen.
SELECT max(count(*))
FROM loans
GROUP BY member_id;
Was psql ausgibt
ERROR: aggregate function calls cannot be nestedWarum, und die Lösung
Gesucht war die höchste Zahl von Ausleihen je Mitglied, aber das Argument eines Aggregats darf kein weiteres Aggregat enthalten. Berechnen Sie die Zahlen je Gruppe und sortieren Sie sie: SELECT member_id, count(*) AS loans FROM loans GROUP BY member_id ORDER BY loans DESC LIMIT 1. Hier liegen zwei Mitglieder mit 2 gleichauf, also ergänzen Sie einen zweiten Sortierschlüssel, der entscheidet, wer vorn steht.
PostgreSQL im Browser: PGlite 0.5.8 (PostgreSQL 18.3), Apache-2.0 und PostgreSQL License. Lizenz und Quellcode
5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.
Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.