Modul 4 von 6

Datenbanken und SQL

Wenn du aus diesem ganzen Vorbereitungskurs nur ein Modul richtig gut beherrschen könntest, sollte es dieses sein. SAP-Systeme sind im Kern große Datenbanken, und SQL ist die Sprache, in der man mit ihnen redet. Auf dieser Seite läuft eine echte SQLite-Datenbank direkt in deinem Browser - jede Abfrage, die du tippst, wird wirklich ausgeführt.

Fortschritt in diesem Modul 0 von 8 Lektionen
Die Übungsdatenbank

Alle Beispiele arbeiten auf demselben kleinen ERP-Datenbestand: Kunden erteilen Aufträge, ein Auftrag besteht aus mehreren Positionen, jede Position verweist auf ein Produkt. Genau dieser Zuschnitt begegnet dir in SAP wieder - dort nur mit ein paar hundert Feldern mehr.

1. Warum überhaupt Datenbanken?

Man könnte Daten auch in Dateien speichern - viele Unternehmen tun das bis heute mit Excel. Bei mehr als einem Benutzer und mehr als ein paar tausend Zeilen bricht das aber zusammen.

Problem in einer TabellendateiWie eine Datenbank es löst
Zwei Leute öffnen die Datei und speichern gleichzeitig - eine Änderung geht verloren Mehrbenutzerbetrieb mit Sperren und Transaktionen
Dieselbe Kundenadresse steht an 40 Stellen, 3 davon veraltet Jeder Wert steht genau einmal, alles andere verweist darauf
Jemand tippt in ein Zahlenfeld „ca. 50“ Datentypen und Regeln verhindern ungültige Werte
Der Rechner stürzt mitten im Speichern ab Eine Änderung wird ganz oder gar nicht ausgeführt
Suchen dauert bei 500 000 Zeilen minutenlang Indizes finden den Treffer in Millisekunden
Jeder kann alles sehen und ändern Rechte je Benutzer, Tabelle und sogar Spalte
Zwei Begriffe, die gern verwechselt werden

Die Datenbank ist der Datenbestand selbst. Das Datenbankmanagementsystem (DBMS) ist die Software, die ihn verwaltet - zum Beispiel SAP HANA, PostgreSQL, Oracle oder SQLite. Zusammen bilden sie das Datenbanksystem. In Klausuren wird nach dieser Abgrenzung regelmäßig gefragt.

2. Das relationale Modell

Fast alle Unternehmensdatenbanken sind relational: Die Daten liegen in Tabellen, und die Tabellen sind über Werte miteinander verknüpft.

FachbegriffAlltagswortBedeutung
RelationTabelleeine Sammlung gleichartiger Datensätze
TupelZeile / Datensatzein einzelner Kunde, ein einzelner Auftrag
AttributSpalte / Feldeine Eigenschaft, etwa der Ort
DomäneWertebereichwelche Werte erlaubt sind, etwa Datum

Primärschlüssel und Fremdschlüssel

Der Primärschlüssel macht jede Zeile eindeutig identifizierbar. Er darf nie leer sein und nie doppelt vorkommen. Ein Fremdschlüssel ist ein Feld, das auf den Primärschlüssel einer anderen Tabelle zeigt - so entstehen die Verbindungen.

kunden                          auftraege
┌────────────┬────────────┐     ┌────────────┬───────────┐
│ kunden_id  │ name       │     │ auftrag_id │ kunden_id │
│ (Primär)   │            │     │ (Primär)   │ (Fremd)   │
├────────────┼────────────┤     ├────────────┼───────────┤
│ 1          │ Meier GmbH │◄────┤ 1          │ 1         │
│ 2          │ Schulz AG  │     │ 5          │ 1         │
└────────────┴────────────┘     └────────────┴───────────┘
                                 Auftrag 1 und 5 gehören zu Kunde 1
Referentielle Integrität

Die Datenbank wacht darüber, dass ein Fremdschlüssel immer auf eine tatsächlich vorhandene Zeile zeigt. Sie verhindert also, dass man einen Auftrag für Kunde 99 anlegt, wenn es diesen Kunden nicht gibt - und dass man einen Kunden löscht, zu dem noch Aufträge existieren. Das ist keine Schikane, sondern der Schutz davor, dass die Datenbank in einen widersprüchlichen Zustand gerät.

Beziehungsarten

1:1

Ein Mitarbeiter hat genau einen Dienstwagen. Selten - meist packt man solche Daten in dieselbe Tabelle.

1:n (der Normalfall)

Ein Kunde hat viele Aufträge, ein Auftrag gehört zu genau einem Kunden. Der Fremdschlüssel steht auf der n-Seite, also beim Auftrag.

n:m

Ein Auftrag enthält viele Produkte, ein Produkt kommt in vielen Aufträgen vor. Das lässt sich nicht direkt abbilden.

Auflösung von n:m

Man schiebt eine Zwischentabelle ein - hier positionen. Sie hat zwei Fremdschlüssel und wird so zu zwei 1:n-Beziehungen.

3. ER-Modellierung

Bevor eine Tabelle angelegt wird, zeichnet man das Modell. Das Entity-Relationship-Diagramm zeigt, welche Dinge es gibt und wie sie zusammenhängen - noch ohne technische Details.

  • Entität: ein Ding, über das Daten gespeichert werden (Kunde, Produkt)
  • Attribut: eine Eigenschaft davon (Name, Preis)
  • Beziehung: wie Entitäten zusammenhängen (Kunde erteilt Auftrag)
  • Kardinalität: wie viele auf jeder Seite stehen (1:n, n:m)
┌──────────┐            ┌───────────┐            ┌───────────┐
│  KUNDE   │ 1        n │  AUFTRAG  │ 1        n │ POSITION  │
│          ├────────────┤           ├────────────┤           │
│ kunden_id│  erteilt   │auftrag_id │  enthält   │position_id│
│ name     │            │ datum     │            │ menge     │
│ ort      │            │ status    │            │           │
└──────────┘            └───────────┘            └─────┬─────┘
                                                       │ n
                                                       │
                                                  ┌────┴──────┐
                                                  │  PRODUKT  │ 1
                                                  │produkt_id │
                                                  │bezeichnung│
                                                  │nettopreis │
                                                  └───────────┘
So gehst du bei einer Modellierungsaufgabe vor
  1. Text durchlesen und alle Substantive markieren - das sind Kandidaten für Entitäten.
  2. Verben markieren - daraus werden die Beziehungen.
  3. Für jede Entität den Primärschlüssel festlegen.
  4. Kardinalitäten bestimmen: Frag für jede Richtung einzeln "wie viele?".
  5. Jede n:m-Beziehung durch eine Zwischentabelle auflösen.

4. Normalisierung

Normalisierung ist das systematische Zerlegen von Tabellen, damit jede Information nur an einer einzigen Stelle steht. Klick dich durch die vier Schritte und beobachte, welches Problem dabei jeweils verschwindet.

Die drei Normalformen als Merksatz

Der Klassiker für die Prüfung

Jedes Nichtschlüsselattribut hängt ab vom Schlüssel (1. NF), vom ganzen Schlüssel (2. NF) und von nichts als dem Schlüssel (3. NF).

NormalformBedingungVerletzt, wenn …
1. NF alle Werte sind atomar in einer Zelle mehrere Werte stehen, etwa „Schraube, Mutter“
2. NF 1. NF und keine Abhängigkeit von einem Teil des Schlüssels der Schlüssel aus zwei Feldern besteht und ein Attribut nur von einem abhängt
3. NF 2. NF und keine transitiven Abhängigkeiten ein Attribut von einem anderen Nichtschlüsselattribut abhängt (Ort hängt am Kunden, nicht am Auftrag)

Die drei Anomalien

  • Einfügeanomalie: Ein neuer Kunde lässt sich nicht anlegen, solange er keinen Auftrag hat.
  • Änderungsanomalie: Eine Adressänderung muss an vielen Stellen nachgezogen werden; wird eine vergessen, widersprechen sich die Daten.
  • Löschanomalie: Mit dem letzten Auftrag verschwindet auch die Adresse des Kunden.
Wenn man bewusst wieder denormalisiert

Normalisierung spart Speicher und verhindert Widersprüche - aber sie erzwingt viele JOINs beim Lesen. In Auswertungssystemen (Data Warehouse, SAP BW) nimmt man deshalb bewusst Redundanz in Kauf, um Berichte schneller zu machen. Das ist kein Fehler, sondern eine Abwägung: normalisieren für das Erfassen, denormalisieren für das Auswerten.

5. SQL: Daten abfragen

SQL (Structured Query Language) ist die Sprache aller relationalen Datenbanken. Das Besondere: Du beschreibst was du willst, nicht wie es geholt werden soll. Den Weg sucht sich die Datenbank selbst.

SELECT   spalten          -- welche Spalten sollen erscheinen
FROM     tabelle          -- woher
WHERE    bedingung        -- welche Zeilen
GROUP BY spalte           -- zusammenfassen nach
HAVING   bedingung        -- Filter auf die Gruppen
ORDER BY spalte           -- Sortierung
LIMIT    anzahl;          -- nur die ersten n
Die Reihenfolge der Verarbeitung

Geschrieben wird SELECT zuerst, ausgeführt wird es fast zuletzt. Die Datenbank arbeitet in dieser Reihenfolge: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT. Daraus folgt der wichtigste praktische Merksatz: WHERE filtert einzelne Zeilen, HAVING filtert fertige Gruppen.

Probier es aus

Unten steht eine echte Datenbank. Ändere die Abfrage und drück auf Ausführen - Strg+Eingabe geht auch.

Die wichtigsten WHERE-Bedingungen

SchreibweiseBedeutung
= <> < > <= >=Vergleiche. Ungleich ist <>, nicht !=
BETWEEN 10 AND 20Bereich, Grenzen eingeschlossen
IN ('DE', 'AT')einer aus einer Liste
LIKE 'Meier%'Textmuster; % steht für beliebig viele Zeichen
IS NULLFeld ist leer
AND OR NOTBedingungen verknüpfen
NULL ist nicht null und nicht ""

NULL bedeutet „unbekannt“. Deshalb ergibt WHERE betreuer = NULL nie einen Treffer - denn unbekannt ist auch nicht gleich unbekannt. Richtig ist WHERE betreuer IS NULL. Das ist einer der häufigsten SQL-Fehler überhaupt, und in Klausuren eine gern gestellte Fangfrage. Probier es unten aus: In der Tabelle kunden gibt es genau einen Kunden ohne Betreuer.

6. SQL: Tabellen verknüpfen

Weil normalisierte Daten über mehrere Tabellen verteilt sind, muss man sie zum Lesen wieder zusammenführen. Das macht der JOIN.

SELECT k.name, a.auftrag_id, a.datum
FROM   kunden k
JOIN   auftraege a ON a.kunden_id = k.kunden_id;
--                    └──────────┬──────────┘
--                    die Verbindungsbedingung:
--                    welcher Fremdschlüssel zeigt auf welchen Primärschlüssel
Die JOIN-Arten
  • INNER JOIN (oder einfach JOIN): nur Zeilen, die auf beiden Seiten einen Partner haben. Der Normalfall.
  • LEFT JOIN: alle Zeilen der linken Tabelle, auch ohne Partner - fehlende Werte werden zu NULL.
  • RIGHT JOIN: dasselbe umgekehrt.
  • FULL OUTER JOIN: alles von beiden Seiten.
Wann LEFT JOIN der entscheidende Unterschied ist

Frage: „Wie viele Aufträge hat jeder Kunde?“ Mit INNER JOIN tauchen Kunden ohne Aufträge gar nicht auf - die Antwort ist unvollständig, und niemand merkt es. Mit LEFT JOIN erscheinen sie mit dem Wert 0. Immer wenn die Frage „alle X, auch die ohne Y“ lautet, brauchst du einen LEFT JOIN.

Aufgabe: drei Tabellen verbinden

Zeig für jeden Auftrag die Auftragsnummer, den Kundennamen und die Produktbezeichnung jeder Position. Du brauchst dafür alle vier Tabellen außer mitarbeiter.

Lösung anzeigen
SELECT a.auftrag_id, k.name, p.bezeichnung
FROM   auftraege a
JOIN   kunden     k  ON k.kunden_id  = a.kunden_id
JOIN   positionen po ON po.auftrag_id = a.auftrag_id
JOIN   produkte   p  ON p.produkt_id  = po.produkt_id;

Denkweise: Man hangelt sich an den Fremdschlüsseln entlang. Vom Auftrag kommt man zum Kunden (über kunden_id) und zu den Positionen (über auftrag_id); von der Position zum Produkt (über produkt_id). Die kurzen Buchstaben hinter den Tabellennamen sind Aliase - ohne sie wird die Abfrage schnell unleserlich.

7. SQL: Gruppieren und auswerten

Hier wird SQL zum Werkzeug für Auswertungen - genau das, was im Berufsalltag der Wirtschaftsinformatik den größten Teil ausmacht.

FunktionWas sie tut
COUNT(*)zählt Zeilen
SUM(spalte)summiert
AVG(spalte)Durchschnitt
MIN / MAXkleinster und größter Wert
ROUND(wert, 2)rundet auf 2 Nachkommastellen
COUNT(*) und COUNT(spalte) zählen unterschiedlich

COUNT(*) zählt alle Zeilen. COUNT(betreuer) zählt nur die Zeilen, in denen dieses Feld nicht NULL ist. Der Unterschied fällt erst auf, wenn Daten fehlen - und dann stimmt der Bericht nicht mehr.

Diese Abfrage Zeile für Zeile
  1. FROM und JOIN führen Kunden, Aufträge und Positionen zu einer breiten Tabelle zusammen.
  2. WHERE wirft stornierte Aufträge heraus - vor dem Gruppieren, sie zählen also nirgends mit.
  3. GROUP BY fasst alle Zeilen eines Kunden zu einer zusammen.
  4. SUM und COUNT rechnen innerhalb jeder Gruppe. DISTINCT ist nötig, weil dieselbe Auftragsnummer durch die Positionen mehrfach vorkommt.
  5. HAVING filtert die fertigen Gruppen - das ginge mit WHERE nicht, weil die Summe dort noch gar nicht existiert.

Aufgabe: Umsatz je Produktkategorie

Ermittle je kategorie den Gesamtumsatz (Menge mal Einzelpreis) über alle Positionen, absteigend sortiert. Nenn die Summenspalte umsatz.

Lösung anzeigen
SELECT p.kategorie,
       ROUND(SUM(po.menge * po.einzelpreis), 2) AS umsatz
FROM   positionen po
JOIN   produkte p ON p.produkt_id = po.produkt_id
GROUP BY p.kategorie
ORDER BY umsatz DESC;

Die Regel zu GROUP BY: Jede Spalte, die im SELECT steht und nicht in einer Aggregatfunktion wie SUM steckt, muss im GROUP BY auftauchen. Sonst wüsste die Datenbank nicht, welchen der vielen Werte einer Gruppe sie anzeigen soll.

Unterabfragen

Eine Abfrage darf innerhalb einer anderen stehen. Damit beantwortet man Fragen, die sich auf ein Zwischenergebnis beziehen - etwa „alle Produkte, die teurer sind als der Durchschnitt“.

8. Daten ändern, Transaktionen und ACID

Die drei ändernden Befehle

INSERT INTO kunden (kunden_id, name, ort, land, kunde_seit, betreuer)
VALUES (13, 'Nova Systems', 'Leipzig', 'DE', '2025-01-15', 1);

UPDATE produkte
SET    nettopreis = nettopreis * 1.05
WHERE  kategorie = 'Antrieb';

DELETE FROM auftraege
WHERE  status = 'storniert';
Die teuerste vergessene Zeile der Welt

Ohne WHERE gilt ein UPDATE oder DELETE für alle Zeilen der Tabelle. DELETE FROM kunden; leert die gesamte Kundentabelle - ohne Nachfrage. Angewohnheit, die dich einmal retten wird: Schreib die Anweisung zuerst als SELECT mit demselben WHERE, sieh dir an, welche Zeilen betroffen wären, und tausch dann SELECT gegen UPDATE oder DELETE. Auf dieser Seite ist das gefahrlos - die Datenbank wird bei jedem Ausführen neu aufgebaut.

Transaktionen

Eine Transaktion fasst mehrere Anweisungen zu einer Einheit zusammen, die entweder komplett oder gar nicht ausgeführt wird.

Die Überweisung

Konto A wird belastet, Konto B bekommt gutgeschrieben. Fällt der Server zwischen den beiden Schritten aus, ist das Geld verschwunden. Eine Transaktion verhindert das: Entweder passieren beide Buchungen oder keine.

ACID - vier Buchstaben, die du auswendig können solltest

BuchstabeBedeutungWas es garantiert
AAtomaritätAlles oder nichts - halbe Transaktionen gibt es nicht.
CKonsistenzVorher und nachher gelten alle Regeln, etwa Fremdschlüssel.
IIsolationParallele Transaktionen stören sich nicht gegenseitig.
DDauerhaftigkeitWas bestätigt wurde, überlebt auch einen Stromausfall.

Indizes

Ein Index ist eine zusätzliche sortierte Struktur über einer Spalte - wie das Stichwortverzeichnis hinten im Buch. Ohne Index muss die Datenbank die ganze Tabelle lesen (O(n)), mit Index findet sie den Treffer in O(log n).

Warum man nicht einfach alles indiziert

Jeder Index muss bei jedem INSERT, UPDATE und DELETE mitgepflegt werden. Indizes machen also das Lesen schneller und das Schreiben langsamer, und sie brauchen Speicher. Faustregel: Spalten indizieren, nach denen häufig gefiltert oder sortiert wird - Primär- und Fremdschlüssel bekommen ihn meist automatisch. Das ist genau die Abwägung aus Modul 3.

SQL-Übungen

Jede Aufgabe wird automatisch geprüft: Deine Abfrage wird mit einer Musterlösung verglichen. Der Weg ist egal - es zählt nur, dass dasselbe Ergebnis herauskommt.

Aufgabe 1 · leicht

Zeig alle Produkte der Kategorie Steuerung mit Bezeichnung und Preis, sortiert nach Preis absteigend.

Aufgabe 2 · leicht

Wie viele Kunden gibt es je Land? Zwei Spalten: land und anzahl.

Aufgabe 3 · mittel

Zeig alle Kunden, deren Name mit W beginnt oder die in Berlin sitzen.

Aufgabe 4 · mittel

Ermittle für jeden Betreuer aus der Tabelle mitarbeiter, wie viele Kunden er betreut. Spalten: name und kundenzahl. Auch Mitarbeiter ohne Kunden sollen erscheinen - überleg, welche JOIN-Art du dafür brauchst.

Lösung anzeigen
SELECT m.name, COUNT(k.kunden_id) AS kundenzahl
FROM   mitarbeiter m
LEFT JOIN kunden k ON k.betreuer = m.mitarbeiter_id
GROUP BY m.mitarbeiter_id, m.name;

Zwei Feinheiten: Der LEFT JOIN sorgt dafür, dass auch Mitarbeiter aus Support und Einkauf erscheinen - mit 0. Und es muss COUNT(k.kunden_id) heißen, nicht COUNT(*): Bei einem Mitarbeiter ohne Kunden entsteht durch den LEFT JOIN eine Zeile mit lauter NULL, die COUNT(*) als 1 zählen würde. COUNT(spalte) überspringt NULL und liefert korrekt 0.

Aufgabe 5 · schwer

Welche drei Produkte haben den höchsten Gesamtumsatz (Menge mal Einzelpreis über alle Positionen)? Spalten: bezeichnung und umsatz, absteigend, nur die obersten drei.

Lösung anzeigen
SELECT p.bezeichnung,
       ROUND(SUM(po.menge * po.einzelpreis), 2) AS umsatz
FROM   positionen po
JOIN   produkte p ON p.produkt_id = po.produkt_id
GROUP BY p.produkt_id, p.bezeichnung
ORDER BY umsatz DESC
LIMIT 3;

Gruppiert wird nach produkt_id und nicht nur nach der Bezeichnung - falls zwei Produkte einmal gleich heißen, bleiben sie so trotzdem getrennt. Bei Auswertungen immer nach dem Schlüssel gruppieren, nicht nach dem Anzeigetext.

Selbsttest

Karteikarten zu diesem Modul

Wenn du tiefer einsteigen willst

  • sqlbolt.com und SQLZoo - interaktive SQL-Kurse, beide kostenlos. Ideal, wenn du nach diesem Modul einfach weiterüben willst.
  • "SQL - der Grundkurs" von Ralf Adams - deutschsprachig und praxisnah.
  • openSAP - SAP bietet kostenlose Onlinekurse an, unter anderem zu HANA und zur Datenmodellierung. Ein Kurs vor Studienbeginn macht sich im Gespräch mit Kollegen gut bezahlt.