Modulseite

Lektion 1 von 9

Eine Datenbank lesen: Tabellen, Schlüssel und ERD

Du benennst Tabellen, Datensätze, Felder und Datentypen, unterscheidest Primär- und Fremdschlüssel und liest ein ER-Diagramm mit Kardinalitäten.

ca. 40 Min.0/3 Checks gelöst

Worum es geht

Lea ist im 3. Lehrjahr ihrer Ausbildung zur Mediamatikerin bei der Aareklang GmbH in Olten. Die Firma organisiert ein Musikfestival und eine Konzertreihe, verkauft Tickets und Merchandise über die eigene Website und verschickt einen Newsletter. Am Montagmorgen steht die Marketingleiterin Mira Haller an Leas Pult: "Kannst du mir bis Mittag sagen, wer schon Tickets für Sturmfront gekauft hat? Die Agentur braucht sonst drei Tage dafür."

Lea öffnet phpMyAdmin und sieht 14 Tabellen mit Namen wie kunde, konzert, ticket und newsletter_abo. Bevor sie auch nur eine Zeile SQL schreibt, muss sie eine Frage klären: Wo steht was, und wie hängen die Tabellen zusammen? Genau das lernst du in dieser Lektion. Wer den Aufbau einer Datenbank lesen kann, schreibt später Abfragen in Minuten. Wer ihn nicht versteht, rät und produziert falsche Zahlen.

Am Ende dieser Lektion kannst du Tabellen, Spalten, Datentypen sowie Primär- und Fremdschlüssel benennen, ein ER-Diagramm lesen und einem Datensatz über mehrere Tabellen hinweg folgen. Im ersten Labor findest du selbst heraus, wem ein bestimmtes Ticket gehört.

Vom Excel-Blatt zur Tabelle

Du kennst Excel. Eine Tabelle in einer relationalen Datenbank sieht ähnlich aus, hat aber strengere Regeln:

BegriffBedeutungVergleich mit Excel
TabelleSammlung gleichartiger Dinge, z.B. alle Kundinnen und Kundenein Tabellenblatt
Datensatz (Zeile)ein einzelnes Ding, z.B. die Kundin Andrea Studereine Zeile
Feld (Spalte, Attribut)eine Eigenschaft, z.B. orteine Spalte mit Überschrift
Datentypwelche Art von Wert in die Spalte darfgibt es in Excel nur lose (Zellformat)

So sieht ein Ausschnitt der Tabelle kunde bei Aareklang aus:

Text
kunde_id | vorname | nachname | email                    | plz  | ort       | kanton
---------+---------+----------+--------------------------+------+-----------+-------
1        | Andrea  | Studer   | andrea.studer@bluewin.ch | 4600 | Olten     | SO
2        | Marco   | Bianchi  | m.bianchi@gmx.ch         | 5000 | Aarau     | AG
12       | Daniel  | Brunner  | NULL                     | 4600 | Olten     | SO

Jede Spalte hat einen festen Datentyp. Die Datenbank lehnt Werte ab, die nicht passen, oder wandelt sie um. Die wichtigsten Typen in MySQL und MariaDB:

TypWofürBeispiel
INTganze Zahlen, IDs, Mengen1017
DECIMAL(6,2)Geldbeträge, exakt59.00
VARCHAR(n)Text bis n Zeichen'Olten'
CHAR(4)Text mit fester Länge, z.B. PLZ'4600'
DATE / DATETIMEDatum bzw. Datum mit Uhrzeit'2026-12-05'
TINYINT(1) / BOOLEANja/nein als 1/01
TEXTlange Texte, z.B. Blogbeiträge...

Bei Daniel Brunner steht bei der E-Mail NULL. NULL heisst nicht "leerer Text", sondern "kein Wert bekannt". Daniel hat sein Ticket an einer Vorverkaufsstelle gekauft und keine E-Mail angegeben. Mit NULL wirst du in Lektion 3 noch genauer arbeiten.

Warum nicht einfach alles in eine einzige grosse Tabelle schreiben, wie es viele in Excel tun? Stell dir eine Ticketliste vor, in der bei jedem Ticket Name, Adresse und E-Mail der Käuferin stehen. Andrea Studer hat drei Tickets gekauft, also steht ihre Adresse dreimal da. Zieht sie um, musst du drei Zeilen ändern. Vergisst du eine, hat die Datenbank zwei verschiedene Adressen für dieselbe Person, und niemand weiss mehr, welche stimmt. Relationale Datenbanken speichern deshalb jede Information nur einmal und verbinden die Tabellen über Schlüssel.

Primärschlüssel und Fremdschlüssel

Der Primärschlüssel (Primary Key, PK) identifiziert jeden Datensatz einer Tabelle eindeutig. Bei Aareklang heisst er meistens <tabelle>_id, in WordPress oft einfach ID. Ein guter Primärschlüssel ist

  • eindeutig: keine zwei Zeilen haben denselben Wert,
  • nie leer: NULL ist nicht erlaubt,
  • stabil: er ändert sich nie, auch wenn die Person umzieht oder heiratet.

Darum nimmt man selten den Namen oder die E-Mail als Schlüssel, sondern eine fortlaufende Nummer, die die Datenbank selbst vergibt (AUTO_INCREMENT). Zwei Personen können "Marco Bianchi" heissen, und E-Mail-Adressen ändern sich.

Der Fremdschlüssel (Foreign Key, FK) ist eine Spalte, die auf den Primärschlüssel einer anderen Tabelle zeigt. In der Tabelle ticket steht nicht der Name der Käuferin, sondern nur ihre Nummer:

Text
ticket_id | konzert_id | kunde_id | kategorie | preis_chf | gekauft_am
----------+------------+----------+-----------+-----------+-----------
1001      | 1          | 1        | Stehplatz | 59        | 2026-08-14
1017      | 4          | 12       | Stehplatz | 59        | 2026-09-25

ticket.kunde_id = 12 bedeutet: "Dieses Ticket gehört dem Kunden mit kunde_id 12 in der Tabelle kunde." ticket.konzert_id = 4 zeigt auf das Konzert Nummer 4. Wie bei einem Verweis im Bibliothekskatalog ("siehe Regal 4") steht die Information nicht doppelt, sondern es wird auf sie gezeigt.

Ist der Fremdschlüssel in der Datenbank technisch eingerichtet (Constraint), sorgt sie für referentielle Integrität: Ein Ticket für den Kunden 99, den es nicht gibt, wird abgelehnt, und ein Kunde mit Tickets lässt sich nicht einfach löschen.

Check 1 · Eine AntwortEinstieg

In der Tabelle ticket steht die Zeile

Text
ticket_id | konzert_id | kunde_id | kategorie | preis_chf
1009      | 2          | 3        | Sitzplatz | 79

Was sagt der Wert 3 in der Spalte kunde_id aus?

Das ER-Diagramm lesen

Ein Entity-Relationship-Diagramm (ERD) ist die Landkarte der Datenbank. Jedes Rechteck ist eine Tabelle, jede Linie eine Beziehung. Die Enden der Linien zeigen die Kardinalität, also wie viele Datensätze auf jeder Seite beteiligt sind. So sieht der Kern der Aareklang-Datenbank aus:

Die Symbole an den Linienenden heissen Krähenfuss-Notation:

Symbol am LinienendeBedeutung
zwei Striche (||)genau eins
Kreis und Krähenfuss (o{)null, eins oder viele
Strich und Krähenfuss (|{)eins oder viele

Lies jede Linie in beide Richtungen als ganzen Satz:

  • "Ein Kunde kauft null bis viele Tickets. Ein Ticket gehört zu genau einem Kunden." Das ist eine 1:n-Beziehung.
  • "Eine Bestellung enthält mindestens eine Position. Ein Artikel kommt in null bis vielen Positionen vor."

Interessant ist die Tabelle position. Eine Bestellung kann viele Artikel enthalten, und ein Artikel kann in vielen Bestellungen vorkommen. Das ist eine n:m-Beziehung. Relationale Datenbanken lösen sie immer mit einer Zwischentabelle auf, die zwei Fremdschlüssel hat. Hier bilden bestellung_id und artikel_id zusammen den Primärschlüssel, denn derselbe Artikel soll in derselben Bestellung nur einmal als Zeile vorkommen (die Menge steht in menge).

Check 2 · Eine AntwortFortgeschritten

Im ERD der Aareklang-Datenbank steht zwischen bestellung und position die Linie bestellung ||--|{ position. Wie liest du sie korrekt?

Die Datenbank in phpMyAdmin und im CMS erkunden

In phpMyAdmin klickst du links auf die Datenbank und dann auf eine Tabelle. Im Reiter Struktur siehst du jede Spalte mit Name, Typ, ob NULL erlaubt ist, und einem Schlüsselsymbol beim Primärschlüssel. Unter Struktur > Beziehungsansicht stehen die eingerichteten Fremdschlüssel. Die Designer-Ansicht (je nach Version unter "Mehr") zeichnet die Tabellen mit Verbindungslinien, ähnlich wie das ERD oben.

Als Mediamatikerin oder Mediamatiker begegnest du besonders oft der Datenbank eines CMS. In WordPress beginnen alle Tabellen mit einem Präfix, meist wp_. Beiträge und Seiten stehen in wp_posts, Benutzerkonten in wp_users. Die Spalte wp_posts.post_author enthält die ID des Benutzers aus wp_users. Das ist logisch ein Fremdschlüssel, obwohl WordPress ihn nicht als Constraint einrichtet. Deshalb zeigt dir der Designer bei WordPress keine Linien: Die Beziehung existiert nur in der Programmlogik, und du musst sie aus den Spaltennamen ablesen.

Die Aareklang-Website hat eine vereinfachte Version davon:

Text
autor (autor_id PK, name, rolle)
beitrag (beitrag_id PK, autor_id FK, titel, status, kategorie, publiziert_am, aufrufe)

Schritt für Schritt: Wem gehört Ticket 1017?

An der Abendkasse meldet sich jemand mit dem Ticket 1017, der Barcode lässt sich nicht scannen. Lea soll herausfinden, für welches Konzert das Ticket gilt und wer es gekauft hat.

Schritt 1: Die richtige Tabelle finden. Das Ticket ist ein Datensatz in ticket. Die Ticketnummer ist dort der Primärschlüssel ticket_id.

Schritt 2: Den Datensatz lesen.

SQL
SELECT * FROM ticket WHERE ticket_id = 1017;
Text
ticket_id | konzert_id | kunde_id | kategorie | preis_chf | gekauft_am | kanal
1017      | 4          | 12       | Stehplatz | 59        | 2026-09-25 | Vorverkaufsstelle

Zwischenergebnis: Es gibt genau eine Zeile (der Primärschlüssel ist eindeutig). Die Spalten konzert_id und kunde_id sind Fremdschlüssel, sie zeigen auf zwei andere Tabellen.

Schritt 3: Dem ersten Fremdschlüssel folgen.

SQL
SELECT titel, buehne, datum FROM konzert WHERE konzert_id = 4;

Ergebnis: Sturmfront | Hauptbühne | 2026-12-05.

Schritt 4: Dem zweiten Fremdschlüssel folgen.

SQL
SELECT vorname, nachname, ort, email FROM kunde WHERE kunde_id = 12;

Ergebnis: Daniel | Brunner | Olten | NULL. Das passt zum Kanal "Vorverkaufsstelle": Dort wurde keine E-Mail erfasst, deshalb kam das Ticket nicht per Mail.

Schritt 5: Ergebnis formulieren. "Ticket 1017 ist ein Stehplatz für Sturmfront am 5. Dezember 2026 auf der Hauptbühne, gekauft von Daniel Brunner aus Olten." Lea hat drei Tabellen gelesen und zweimal einem Fremdschlüssel gefolgt. In Lektion 5 lernst du, wie ein JOIN diese Schritte in eine einzige Abfrage packt.

Jetzt bist du dran. Die Labore in diesem Kurs laufen direkt im Browser mit SQLite. Für SELECT-Abfragen ist die Schreibweise fast identisch mit MySQL und MariaDB, Unterschiede sind jeweils erwähnt.

Check 3 · Code-LaborEinstieg

Labor: Wem gehört Ticket 1012?

An der Abendkasse wird das Ticket 1012 vorgezeigt, der Barcode ist beschädigt. Finde heraus, wer es gekauft hat.

  1. Lies den Datensatz des Tickets in der Tabelle ticket (die erste Abfrage ist schon vorbereitet). Notiere dir die kunde_id.
  2. Folge dem Fremdschlüssel: Hol aus der Tabelle kunde genau diese drei Spalten der Käuferin bzw. des Käufers: vorname, nachname, ort.

Geprüft wird das Ergebnis deiner letzten Abfrage. Einen JOIN brauchst du noch nicht.

Das Labor läuft mit SQLite im Browser. Für diese Aufgabe ist die Syntax identisch mit MySQL und MariaDB.

Code-Labor · SQL · SQLite
Tabellen und Testdaten ansehen
SQL
PRAGMA foreign_keys = ON;
CREATE TABLE kunde (
  kunde_id      INTEGER PRIMARY KEY,
  vorname       VARCHAR(50) NOT NULL,
  nachname      VARCHAR(50) NOT NULL,
  email         VARCHAR(254),
  strasse       VARCHAR(80),
  plz           CHAR(4),
  ort           VARCHAR(50),
  kanton        CHAR(2),
  geburtsdatum  DATE,
  post_ok       INTEGER NOT NULL DEFAULT 1,
  erfasst_am    DATE NOT NULL
);
INSERT INTO kunde VALUES
 (1, 'Andrea', 'Studer', 'andrea.studer@bluewin.ch', 'Ringstrasse 14', '4600', 'Olten', 'SO', '1994-03-12', 1, '2024-05-02'),
 (2, 'Marco', 'Bianchi', 'm.bianchi@gmx.ch', 'Bahnhofstrasse 3', '5000', 'Aarau', 'AG', '1988-11-02', 1, '2024-06-17'),
 (3, 'Sabrina', 'Kälin', 'sabrina.kaelin@outlook.com', 'Gurzelngasse 8', '4500', 'Solothurn', 'SO', '2001-07-25', 0, '2025-01-09'),
 (4, 'Jonas', 'Meier', 'jonas.meier@sunrise.ch', 'Lotzwilstrasse 41', '4900', 'Langenthal', 'BE', '1979-02-14', 1, '2025-03-30'),
 (5, 'Fatima', 'Rexhepi', 'fatima.r@gmail.com', 'Baslerstrasse 120', '4632', 'Trimbach', 'SO', '2005-09-03', 1, '2025-04-12'),
 (6, 'Luca', 'Ferrari', 'luca.ferrari@gmail.com', 'Langstrasse 77', '8004', 'Zürich', 'ZH', '1997-12-30', 1, '2025-06-01'),
 (7, 'Nina', 'Hofstetter', 'nina.hofstetter@bluewin.ch', 'Badstrasse 5', '5400', 'Baden', 'AG', '1991-04-18', 1, '2025-08-23'),
 (8, 'Peter', 'Gisler', 'p.gisler@hispeed.ch', NULL, '6003', 'Luzern', 'LU', '1965-10-08', 1, '2025-11-11'),
 (9, 'Chantal', 'Rochat', 'chantal.rochat@bluewin.ch', 'Rue du Marché 2', '2502', 'Biel/Bienne', 'BE', '1999-01-21', 1, '2026-01-27'),
 (10, 'Reto', 'Zürcher', 'r.zuercher@gmx.ch', 'Dorfstrasse 22', '4614', 'Hägendorf', 'SO', '1983-06-09', 1, '2026-02-14'),
 (11, 'Elif', 'Yilmaz', 'elif.yilmaz@gmail.com', 'Hauptstrasse 61', '4153', 'Reinach BL', 'BL', '2003-08-15', 1, '2026-05-06'),
 (12, 'Daniel', 'Brunner', NULL, 'Aarauerstrasse 30', '4600', 'Olten', 'SO', NULL, 1, '2026-09-25');

CREATE TABLE konzert (
  konzert_id  INTEGER PRIMARY KEY,
  titel       VARCHAR(80) NOT NULL,
  genre       VARCHAR(30) NOT NULL,
  buehne      VARCHAR(30) NOT NULL,
  datum       DATE NOT NULL,
  kapazitaet  INTEGER NOT NULL
);
INSERT INTO konzert VALUES
 (1, 'Glanzlicht', 'Pop', 'Hauptbühne', '2026-11-07', 1200),
 (2, 'Die Aareschwimmer', 'Rock', 'Hauptbühne', '2026-11-14', 1200),
 (3, 'Velvet Mondays', 'Jazz', 'Kleine Bühne', '2026-11-20', 300),
 (4, 'Sturmfront', 'Rock', 'Hauptbühne', '2026-12-05', 1200),
 (5, 'Lumi & Band', 'Singer-Songwriter', 'Kleine Bühne', '2026-12-11', 300),
 (6, 'Bergwerk Brass', 'Brass', 'Zelt', '2026-12-19', 600),
 (7, 'Neonherz', 'Elektro', 'Zelt', '2027-01-15', 600),
 (8, 'Kaktus Quartett', 'Jazz', 'Kleine Bühne', '2027-01-22', 300);

CREATE TABLE ticket (
  ticket_id   INTEGER PRIMARY KEY,
  konzert_id  INTEGER NOT NULL REFERENCES konzert(konzert_id),
  kunde_id    INTEGER NOT NULL REFERENCES kunde(kunde_id),
  kategorie   VARCHAR(20) NOT NULL,
  preis_chf   DECIMAL(6,2) NOT NULL,
  gekauft_am  DATE NOT NULL,
  kanal       VARCHAR(20) NOT NULL
);
INSERT INTO ticket VALUES
 (1001, 1, 1, 'Stehplatz', 59, '2026-08-14', 'Web'),
 (1002, 1, 2, 'Sitzplatz', 79, '2026-08-15', 'Web'),
 (1003, 1, 5, 'Stehplatz', 59, '2026-09-02', 'Web'),
 (1004, 1, 7, 'VIP', 129, '2026-09-10', 'Web'),
 (1005, 1, 1, 'Stehplatz', 59, '2026-08-14', 'Web'),
 (1006, 2, 4, 'Stehplatz', 59, '2026-08-20', 'Web'),
 (1007, 2, 10, 'Stehplatz', 59, '2026-08-21', 'Web'),
 (1008, 2, 10, 'Stehplatz', 59, '2026-08-21', 'Web'),
 (1009, 2, 3, 'Sitzplatz', 79, '2026-09-05', 'Web'),
 (1010, 2, 6, 'Sitzplatz', 79, '2026-09-18', 'Web'),
 (1011, 3, 3, 'Sitzplatz', 39, '2026-09-01', 'Web'),
 (1012, 3, 9, 'Sitzplatz', 39, '2026-09-03', 'Web'),
 (1013, 3, 2, 'Sitzplatz', 39, '2026-09-22', 'Web'),
 (1014, 4, 4, 'Stehplatz', 59, '2026-09-06', 'Web'),
 (1015, 4, 6, 'Stehplatz', 59, '2026-09-07', 'Web'),
 (1016, 4, 1, 'Sitzplatz', 79, '2026-09-12', 'Web'),
 (1017, 4, 12, 'Stehplatz', 59, '2026-09-25', 'Vorverkaufsstelle'),
 (1018, 4, 7, 'Stehplatz', 59, '2026-09-28', 'Web'),
 (1019, 5, 9, 'Sitzplatz', 39, '2026-09-15', 'Web'),
 (1020, 5, 5, 'Sitzplatz', 39, '2026-09-30', 'Web'),
 (1021, 6, 10, 'Stehplatz', 45, '2026-09-19', 'Web'),
 (1022, 6, 2, 'Stehplatz', 45, '2026-10-01', 'Web');
Strg + Enter

Typische Fehler

  1. Fremdschlüssel mit dem eigenen Primärschlüssel verwechseln. ticket.kunde_id = 12 heisst nicht "Ticket Nummer 12", sondern "gehört Kunde 12". Korrektur: Lies den Spaltennamen ganz und frag dich, auf welche Tabelle er zeigt.
  2. Kardinalität nur in eine Richtung lesen. "Kunde zu Ticket ist 1:n" stimmt erst, wenn du auch geprüft hast, dass ein Ticket genau einem Kunden gehört. Korrektur: Jede Linie zweimal als Satz lesen.
  3. Name oder E-Mail als eindeutig betrachten. Zwei Personen können gleich heissen, und Daniel Brunner hat gar keine E-Mail. Korrektur: Datensätze immer über den Primärschlüssel identifizieren.
  4. NULL für einen leeren Text oder für 0 halten. NULL bedeutet "unbekannt". Ein leerer Text '' ist dagegen ein bekannter, leerer Wert. Korrektur: Bei jeder Spalte prüfen, ob NULL erlaubt ist (Struktur-Reiter, Spalte "Null").
  5. Bei einem CMS nach Constraint-Linien suchen. WordPress richtet keine Fremdschlüssel-Constraints ein. Korrektur: Beziehungen aus Spaltennamen wie post_author oder post_id ableiten.

Zusammenfassung

  • Eine relationale Datenbank besteht aus Tabellen mit Datensätzen (Zeilen) und Feldern (Spalten). Jede Spalte hat einen Datentyp.
  • Jede Information wird nur einmal gespeichert. Tabellen werden über Schlüssel verbunden statt Daten zu wiederholen.
  • Der Primärschlüssel identifiziert eine Zeile eindeutig, stabil und nie leer.
  • Ein Fremdschlüssel zeigt auf den Primärschlüssel einer anderen Tabelle und steht auf der "viele"-Seite einer 1:n-Beziehung.
  • Im ERD liest du jede Beziehung in beide Richtungen. n:m-Beziehungen werden mit einer Zwischentabelle aufgelöst.
  • In phpMyAdmin helfen Struktur, Beziehungsansicht und Designer. Bei CMS-Datenbanken verraten Spaltennamen die Beziehungen.

Mit deiner eigenen KI vertiefen

Kopiere einen Prompt in Claude, ChatGPT oder Claude Code. Er macht die KI zur Lernbegleitung statt zum Lösungsautomaten.

ERD und Schlüssel verstehen Chat-KI

Du lässt dir Primär- und Fremdschlüssel und das Lesen eines ERD an deinem eigenen Beispiel erklären und prüfst danach dein Verständnis.

Übungsdatenbank mit Claude Code aufsetzen Claude Code

Du richtest dir mit Claude Code als Begleiter eine eigene MariaDB mit phpMyAdmin ein und verstehst jeden Schritt, statt nur Befehle zu kopieren.