Datenbank-Grundbegriffe fuer den IT-Support
ERM, Normalisierung, ACID-Transaktionen, JOINs und Locking verstaendlich erklaert - fuer IT-Support-Faelle im KMU-Alltag.
Warum du als Support das ueberhaupt brauchst
Im Schweizer KMU-Alltag landet fast jedes Problem irgendwann bei einer Datenbank: Das ERP haengt, ein Report zeigt falsche Zahlen, ein Update dauert ewig, oder ein Nutzer meldet “die Anwendung ist eingefroren, seit ich auf Speichern geklickt habe”. Als Erstlinien- oder Zweitlinien-Support musst du keine Datenbanken designen koennen - aber du musst verstehen, was hinter der Fehlermeldung passiert, damit du entscheiden kannst: Selbst loesen, Applikationsteam einschalten oder DBA/Hersteller-Support eskalieren.
Dieser Artikel gibt dir das Fundament: Wie Daten in Tabellen organisiert sind, warum Normalisierung existiert, was eine Transaktion “ACID-sicher” macht, wie du SELECT/JOIN-Abfragen liest (nicht unbedingt selbst schreiben musst), und wie Deadlocks entstehen. Am Ende weisst du, wann ein Ticket “nur” ein Index-Problem ist und wann du wirklich einen Datenbankspezialisten brauchst.
Tabellen, Zeilen, Spalten: Das relationale Grundmodell
Fast jede Business-Anwendung (ERP, CRM, Ticketsystem, Buchhaltung) basiert auf einer relationalen Datenbank (SQL Server, PostgreSQL, MySQL/MariaDB, Oracle). Das Grundprinzip ist simpel:
- Eine Tabelle (Table) speichert eine Art von “Dingen”, z. B.
Kunden,Bestellungen,Artikel. - Eine Zeile (Row/Record) ist ein konkretes Ding, z. B. ein einzelner Kunde.
- Eine Spalte (Column/Field) ist eine Eigenschaft, z. B.
Kundenname,PLZ,Kreditlimit.
-- Beispieltabelle Kunden
CREATE TABLE Kunden (
KundeID INT IDENTITY(1,1) PRIMARY KEY,
Firma NVARCHAR(100) NOT NULL,
PLZ NVARCHAR(10),
Ort NVARCHAR(50),
Kreditlimit DECIMAL(10,2) DEFAULT 0
);
Jede Zeile braucht eine eindeutige Kennung - das ist der Primaerschluessel (Primary Key, kurz PK). Ohne PK kann die Datenbank zwei identische Zeilen nicht unterscheiden, und viele Operationen (Updates, Replikation, Indizierung) werden unmoeglich oder extrem langsam.
Primaerschluessel: Kuenstlich vs. natuerlich
| Typ | Beispiel | Vorteil | Nachteil |
|---|---|---|---|
| Kuenstlicher Schluessel (Surrogate Key) | KundeID INT IDENTITY | Aendert sich nie, kompakt, schnell fuer Joins | Hat keine fachliche Bedeutung |
| Natuerlicher Schluessel | AHV-Nummer, USt-ID, E-Mail | Fachlich sprechend | Kann sich aendern (Namensaenderung, neue E-Mail), Datenschutz-Risiko bei AHV-Nummer |
| GUID/UUID | NEWID() in SQL Server | Global eindeutig, gut fuer verteilte Systeme | Groesser (16 Bytes), fragmentiert Indizes stark |
Fremdschluessel und Beziehungen (ERM)
Der Fremdschluessel (Foreign Key, FK) verknuepft Tabellen: Eine Spalte in Tabelle A verweist auf den Primaerschluessel einer Zeile in Tabelle B. Das Entity-Relationship-Modell (ERM) beschreibt genau diese Beziehungen zwischen “Entitaeten” (Dingen) grafisch, bevor man die eigentlichen Tabellen anlegt.
CREATE TABLE Bestellungen (
BestellID INT IDENTITY(1,1) PRIMARY KEY,
KundeID INT NOT NULL,
Bestelldatum DATE NOT NULL DEFAULT GETDATE(),
CONSTRAINT FK_Bestellungen_Kunden
FOREIGN KEY (KundeID) REFERENCES Kunden(KundeID)
);
Drei Beziehungstypen kommen in der Praxis vor:
- 1:n (eins zu viele) - ein Kunde hat mehrere Bestellungen. Das ist der Standardfall, geloest mit einem FK in der “n”-Tabelle.
- n:m (viele zu viele) - ein Artikel kann in vielen Bestellungen vorkommen, eine Bestellung enthaelt viele Artikel. Das braucht eine Verknuepfungstabelle (auch Junction Table oder Zwischentabelle genannt), z. B.
BestellpositionenmitBestellIDundArtikelIDals zusammengesetztem Schluessel. - 1:1 (eins zu eins) - selten, z. B. ein Mitarbeiter hat genau einen Dienstausweis-Datensatz. Oft wird das aus Performance- oder Sicherheitsgruenden in zwei Tabellen aufgeteilt (z. B. sensible Lohndaten separat mit eigenen Berechtigungen).
-- n:m-Beziehung ueber Verknuepfungstabelle
CREATE TABLE Bestellpositionen (
BestellID INT NOT NULL,
ArtikelID INT NOT NULL,
Menge INT NOT NULL,
PRIMARY KEY (BestellID, ArtikelID),
FOREIGN KEY (BestellID) REFERENCES Bestellungen(BestellID),
FOREIGN KEY (ArtikelID) REFERENCES Artikel(ArtikelID)
);
Normalisierung einfach erklaert (1NF bis 3NF)
Normalisierung ist der systematische Prozess, Redundanz aus Tabellen zu entfernen, damit Daten nicht mehrfach und potenziell widerspruechlich gespeichert werden. Das ist wichtig zu verstehen, wenn du beurteilen musst, ob ein “komisches” Datenbankdesign ein Bug oder Absicht ist.
Ausgangslage: Eine unnormalisierte Tabelle
BestellID | Kunde | Ort | Artikel1 | Artikel2 | ArtikelPreis
1 | Mueller AG | Bern | Schraube | Mutter | 0.10, 0.05
Probleme: Wiederholende Gruppen (Artikel1, Artikel2), mehrere Werte in einer Zelle, Kundendaten werden bei jeder Bestellung wiederholt.
1. Normalform (1NF): Atomare Werte, keine Wiederholungsgruppen
Jede Spalte darf nur einen einzelnen (atomaren) Wert enthalten, keine Listen. Statt Artikel1/Artikel2 brauchst du eine eigene Zeile pro Artikel (siehe Bestellpositionen oben).
2. Normalform (2NF): Voll funktionale Abhaengigkeit vom gesamten Schluessel
Gilt nur relevant bei zusammengesetzten Schluesseln. Jede Nicht-Schluessel-Spalte muss vom gesamten Schluessel abhaengen, nicht nur von einem Teil. Beispiel eines Verstosses: In Bestellpositionen (BestellID, ArtikelID, Menge, ArtikelBezeichnung) haengt ArtikelBezeichnung nur von ArtikelID ab, nicht vom vollen Schluessel (BestellID, ArtikelID). Loesung: ArtikelBezeichnung gehoert in die Tabelle Artikel.
3. Normalform (3NF): Keine transitiven Abhaengigkeiten
Eine Nicht-Schluessel-Spalte darf nicht von einer anderen Nicht-Schluessel-Spalte abhaengen. Beispiel: In Kunden (KundeID, PLZ, Ort) haengt Ort von PLZ ab, nicht direkt vom Schluessel KundeID. Sauberer waere eine eigene Tabelle PLZ_Orte (PLZ, Ort). In der Praxis wird das oft bewusst nicht bis zur letzten Konsequenz durchgezogen (siehe unten).
Trade-off: Normalisiert vs. denormalisiert
| Kriterium | Normalisiert (3NF) | Denormalisiert |
|---|---|---|
| Datenredundanz | Minimal | Bewusst vorhanden |
| Schreibperformance (INSERT/UPDATE) | Gut, da nur ein Ort geaendert wird | Schlechter, mehrere Kopien muessen synchron gehalten werden |
| Leseperformance bei komplexen Reports | Braucht viele JOINs, kann langsam sein | Schnell, da Daten schon “flach” vorliegen |
| Gefahr von Inkonsistenzen | Gering | Hoch, wenn Sync vergessen wird |
| Typischer Einsatz | Transaktionale Systeme (OLTP): ERP, CRM | Data Warehouse, Reporting (OLAP), Caching-Tabellen |
ACID-Transaktionen: Warum Geld nicht verschwindet
Eine Transaktion buendelt mehrere Datenbankoperationen zu einer logischen Einheit, die entweder komplett durchgefuehrt oder komplett verworfen wird. Klassisches Beispiel: Eine Ueberweisung zieht Geld von Konto A ab und schreibt es Konto B gut - beide Schritte muessen zusammen gelingen oder zusammen scheitern.
Die vier ACID-Eigenschaften garantieren das:
- Atomicity (Atomaritaet): Alles oder nichts. Bricht die Transaktion mitten drin ab (Stromausfall, Fehler), werden alle bereits ausgefuehrten Teilschritte zurueckgerollt (Rollback).
- Consistency (Konsistenz): Die Datenbank bewegt sich von einem gueltigen Zustand in einen anderen gueltigen Zustand. Constraints (Fremdschluessel, CHECK-Regeln) werden nie verletzt.
- Isolation (Isolation): Parallele Transaktionen beeinflussen sich nicht gegenseitig, als liefen sie nacheinander ab (auch wenn sie technisch gleichzeitig laufen).
- Durability (Dauerhaftigkeit): Ist eine Transaktion committed (bestaetigt), bleibt sie auch nach einem Stromausfall oder Absturz erhalten - dank Transaktionslog.
BEGIN TRANSACTION;
UPDATE Konten SET Saldo = Saldo - 500 WHERE KontoID = 1;
UPDATE Konten SET Saldo = Saldo + 500 WHERE KontoID = 2;
-- Pruefen, ob beide Updates ok waren
IF @@ERROR = 0
COMMIT TRANSACTION;
ELSE
ROLLBACK TRANSACTION;
Isolationsstufen (Isolation Levels)
Volle Isolation waere am sichersten, aber am langsamsten (jede Transaktion muesste auf jede andere warten). Deshalb gibt es abgestufte Isolation Levels, die einen Kompromiss zwischen Konsistenz und Performance erlauben:
| Isolation Level | Verhindert Dirty Read | Verhindert Non-Repeatable Read | Verhindert Phantom Read | Typischer Einsatz |
|---|---|---|---|---|
| Read Uncommitted | Nein | Nein | Nein | Selten, nur fuer grobe Reports ohne Konsistenzanspruch |
| Read Committed (Standard bei SQL Server) | Ja | Nein | Nein | Standardfall fuer die meisten Anwendungen |
| Repeatable Read | Ja | Ja | Nein | Finanzberichte, die waehrend der Laufzeit stabil bleiben muessen |
| Serializable | Ja | Ja | Ja | Kritische Finanztransaktionen, hoechste Konsistenzanforderung |
SELECT und JOIN: Die Basis, die jeder Support-Mitarbeiter lesen koennen sollte
Du musst kein SQL-Entwickler sein, aber wenn ein Applikationsentwickler dir einen Debug-Query schickt oder du selbst im SQL Server Management Studio (SSMS) nachschauen willst, ob Daten “wirklich fehlen” oder “nur nicht angezeigt werden”, brauchst du diese Grundlagen.
SELECT-Grundgeruest
SELECT KundeID, Firma, Ort
FROM Kunden
WHERE Ort = 'Bern'
ORDER BY Firma ASC;
JOIN: Tabellen verknuepfen
Ein JOIN kombiniert Zeilen aus zwei (oder mehr) Tabellen anhand einer gemeinsamen Spalte, meist Primaer-/Fremdschluessel.
-- INNER JOIN: nur Zeilen, die in BEIDEN Tabellen einen passenden Partner haben
SELECT b.BestellID, k.Firma, b.Bestelldatum
FROM Bestellungen b
INNER JOIN Kunden k ON b.KundeID = k.KundeID;
-- LEFT JOIN: alle Kunden, auch wenn sie noch KEINE Bestellung haben
SELECT k.Firma, b.BestellID
FROM Kunden k
LEFT JOIN Bestellungen b ON k.KundeID = b.KundeID
WHERE b.BestellID IS NULL; -- findet Kunden OHNE Bestellung
| JOIN-Typ | Ergebnis | Typischer Support-Use-Case |
|---|---|---|
| INNER JOIN | Nur Zeilen mit Treffer in beiden Tabellen | ”Zeige alle Bestellungen mit Kundenname” |
| LEFT JOIN | Alle Zeilen der linken Tabelle, plus Treffer aus rechter (sonst NULL) | “Welche Kunden haben noch nie bestellt?” (typische Datenreinigungs-Abfrage) |
| RIGHT JOIN | Spiegelbild von LEFT JOIN | Selten genutzt, meist per LEFT JOIN mit vertauschter Tabellenreihenfolge geloest |
| FULL OUTER JOIN | Alle Zeilen aus beiden Tabellen, egal ob Treffer | Datenabgleich zwischen zwei Systemen (z. B. ERP vs. CRM) |
Deadlocks und Locking: Wenn zwei Transaktionen sich blockieren
Damit ACID-Isolation funktioniert, setzt die Datenbank Sperren (Locks) auf Zeilen, Seiten oder ganze Tabellen, waehrend eine Transaktion sie bearbeitet. Das verhindert, dass zwei Transaktionen gleichzeitig denselben Datensatz inkonsistent aendern.
Ein Deadlock entsteht, wenn zwei Transaktionen sich gegenseitig blockieren: Transaktion A haelt eine Sperre auf Zeile 1 und wartet auf Zeile 2, waehrend Transaktion B eine Sperre auf Zeile 2 haelt und auf Zeile 1 wartet. Keine kann weitermachen.
Transaktion A Transaktion B
------------------------------ ------------------------------
UPDATE Konten SET ... WHERE ID=1 UPDATE Konten SET ... WHERE ID=2
(haelt Lock auf Zeile 1) (haelt Lock auf Zeile 2)
UPDATE Konten SET ... WHERE ID=2 UPDATE Konten SET ... WHERE ID=1
(wartet auf Lock von B) <----> (wartet auf Lock von A)
DEADLOCK!
Die Datenbank erkennt das automatisch (Deadlock Detection) und beendet eine der beiden Transaktionen zwangsweise mit einem Rollback (“Deadlock Victim”) - die andere darf weiterlaufen.
Symptome, die im Ticket landen
- Anwendung “haengt” fuer mehrere Sekunden, dann Fehlermeldung wie “Transaction (Process ID X) was deadlocked on lock resources with another process and has been chosen as the deadlock victim.”
- Timeouts (“Der Vorgang wurde abgebrochen, da das Zeitlimit ueberschritten wurde”)
- Berichte oder Batch-Jobs, die nachts laufen und morgens als fehlgeschlagen gemeldet werden
Erste Diagnoseschritte fuer den Support
-- SQL Server: Aktuelle Sperren und blockierende Sessions anzeigen
SELECT
blocking_session_id AS BlockedBy,
session_id AS BlockedSession,
wait_type,
wait_time,
wait_resource
FROM sys.dm_exec_requests
WHERE blocking_session_id <> 0;
-- Deadlock-Historie aus dem System_Health Extended Events Log auslesen (SQL Server)
SELECT
xed.value('@timestamp', 'datetime2') AS DeadlockZeit,
xed.query('.') AS DeadlockGraph
FROM (
SELECT CAST(target_data AS XML) AS TargetData
FROM sys.dm_xe_session_targets st
JOIN sys.dm_xe_sessions s ON s.address = st.event_session_address
WHERE s.name = 'system_health'
) AS Data
CROSS APPLY TargetData.nodes('RingBufferTarget/event[@name="xml_deadlock_report"]') AS Tab(xed);
Entscheidungsbaum: Selbst loesen oder eskalieren?
Ticket: "Anwendung/Bericht verhaelt sich seltsam bei Datenbankzugriff"
│
├─ Ist es ein einmaliger Timeout/Deadlock, App laeuft danach normal?
│ └─ Ja → Vorfall dokumentieren, Nutzer informieren, beobachten. Kein Eskalationsbedarf.
│
├─ Fehlermeldung nennt explizit "Deadlock" oder "Timeout expired" wiederholt?
│ └─ Ja → Blockierende Sessions pruefen (sys.dm_exec_requests).
│ Tritt es taeglich zur gleichen Zeit auf (z. B. Batch-Job)?
│ ├─ Ja → Applikationsteam/DBA einschalten, Deadlock-Graph mitliefern.
│ └─ Nein, einmalig → Beobachten, Ticket dokumentieren.
│
├─ Nutzer meldet "Daten fehlen" in einem Bericht?
│ └─ Pruefen: Basiert der Bericht auf INNER JOIN? Fehlen nur Datensaetze ohne Referenz?
│ ├─ Ja, JOIN-Logik erklaert es → Kein Bug, Rueckmeldung an Nutzer/Applikationsteam.
│ └─ Nein, Daten fehlen trotz vorhandener Referenz → An DBA/Applikationsteam eskalieren,
│ moeglicher Datenintegritaets- oder Replikationsfehler.
│
├─ Datenbankserver reagiert generell langsam, hohe CPU/IO-Last?
│ └─ Eskalation an DBA: Kandidat fuer Index-Tuning, Statistik-Updates oder Kapazitaetsproblem.
│ Siehe dazu SQL Server Indizes und Tuning.
│
└─ Verdacht auf Datenkorruption, fehlgeschlagene Wiederherstellung, Backup-Probleme?
└─ SOFORT eskalieren (P1). Nichts selbst reparieren, keine Skripte gegen Produktivdaten fahren.
Praxis-Checkliste fuer den Alltag
- Bei jedem “die Anwendung haengt”-Ticket: Zuerst pruefen, ob es sich um eine blockierende Session handelt, bevor du den Anwendungsserver neu startest.
- Bei “Daten fehlen im Bericht”: JOIN-Logik hinterfragen, bevor du einen Datenbankfehler vermutest.
- Bei Loeschfehlern wegen Fremdschluessel-Constraints: Nicht den Constraint deaktivieren, sondern mit dem Anwendungsverantwortlichen klaeren, ob ein Soft-Delete-Flag existiert.
- Bei wiederkehrenden Deadlocks: Deadlock-Graph sichern und mit konkretem Zeitstempel an DBA/Hersteller-Support weiterleiten - “es deadlockt manchmal” hilft niemandem.
- Nie direkt in Produktivtabellen schreiben ohne Transaktion, Backup-Check und Testlauf mit SELECT.
Weiterlernen
- Microsoft Learn: Transaktionen (SQL Server)
- Microsoft Learn: Transaction Locking and Row Versioning Guide
- Microsoft Learn: SET TRANSACTION ISOLATION LEVEL
- PostgreSQL-Dokumentation: Concurrency Control
- Was ist eine Transaktion und ACID? (YouTube)
- SQL Inner Join Tutorial Deutsch (YouTube)
Videos
Kommentare
Frage, Verbesserungsvorschlag oder eigene Erfahrung zu diesem Artikel? Schreib einen Kommentar. Neue Beiträge erscheinen nach kurzer Moderation.
- Lade Kommentare …