CASCADE, RESTRICT und SET NULL im Detail
Ein Fremdschluessel ist kein optionales Dekorationselement, sondern der Mechanismus, mit dem eine Datenbank referentielle Integritaet selbst durchsetzt, statt sich auf Anwendungscode zu verlassen. Dieser Beitrag erklaert die FOREIGN KEY Syntax, das Verhalten von ON DELETE CASCADE, RESTRICT und SET NULL im Detail, die Bedeutung von ON UPDATE, die richtige Indexierung von Fremdschluesselspalten und warum das Deaktivieren von FK-Checks fuer vermeintliche Bequemlichkeit fast immer zu Datenkorruption fuehrt.
Inhaltsverzeichnis
- 1. Was referentielle Integritaet konkret bedeutet
- 2. FOREIGN KEY Syntax Grundlagen
- 3. ON DELETE CASCADE: kontrolliertes Mitloeschen
- 4. ON DELETE RESTRICT und NO ACTION
- 5. ON DELETE SET NULL und SET DEFAULT
- 6. ON UPDATE Verhalten richtig einsetzen
- 7. Warum FK-Checks deaktivieren Daten korrumpiert
- 8. Indexierung von Fremdschluesselspalten
- 9. Vendor-Unterschiede: MySQL, PostgreSQL, SQL Server
- 10. Zusammenfassung
- 11. FAQ
1. Was referentielle Integritaet konkret bedeutet
Referentielle Integritaet bedeutet, dass jeder Wert in einer Fremdschluesselspalte entweder NULL ist oder tatsaechlich auf eine existierende Zeile in der referenzierten Tabelle zeigt. Eine Bestellung mit customer_id = 42 darf nur dann existieren, wenn in der customers-Tabelle tatsaechlich ein Kunde mit der ID 42 vorhanden ist. Ohne diese Garantie entstehen sogenannte verwaiste Datensaetze, Zeilen, die auf nicht mehr existierende Eltern-Datensaetze verweisen, mit Konsequenzen, die sich erst bei der naechsten Abfrage oder Anwendung zeigen.
Ein Fremdschluessel, im SQL-Standard als FOREIGN KEY Constraint definiert, ist der Mechanismus, mit dem die Datenbank selbst diese Garantie durchsetzt, ohne dass Anwendungscode jede einzelne Einfuege-, Aktualisierungs- oder Loeschoperation manuell absichern muss. Diese Durchsetzung auf Datenbankebene ist entscheidend, weil Anwendungscode Bugs enthalten kann, mehrere Anwendungen gleichzeitig auf dieselbe Datenbank zugreifen koennen und manuelle Datenbankaenderungen, etwa durch ein Skript oder einen Administrator, an der Anwendungslogik vollstaendig vorbeigehen. Nur eine Constraint auf Datenbankebene schuetzt konsequent in allen diesen Faellen gleichzeitig.
2. FOREIGN KEY Syntax Grundlagen
Die grundlegende Syntax eines Fremdschluessels besteht aus drei Teilen: der Spalte in der referenzierenden Tabelle, dem Schluesselwort REFERENCES und der Zieltabelle mit ihrer referenzierten Spalte, die fast immer der Primaerschluessel oder zumindest eine UNIQUE-Spalte sein muss. Ein Fremdschluessel kann inline bei der Spaltendefinition deklariert werden oder als separate CONSTRAINT-Klausel am Ende der CREATE TABLE Anweisung, was insbesondere bei zusammengesetzten Fremdschluesseln uebersichtlicher ist und der benannten Constraint einen eigenen, spaeter referenzierbaren Namen gibt.
Ein oft uebersehener Aspekt der Fremdschluessel-Syntax ist, dass die referenzierte Spalte einen eindeutigen Index besitzen muss, entweder als PRIMARY KEY oder als UNIQUE Constraint. Ohne diese Eindeutigkeit koennte ein Fremdschluesselwert mehrdeutig auf mehrere Zeilen zeigen, was die gesamte Garantie der referentiellen Integritaet aushebeln wuerde. Die meisten Datenbank-Engines verweigern deshalb das Anlegen eines Fremdschluessels auf eine Spalte ohne passenden eindeutigen Index von vornherein mit einem Fehler.
-- Basic FOREIGN KEY syntax: inline and as a named constraint
CREATE TABLE customers (
customer_id INT PRIMARY KEY AUTO_INCREMENT,
full_name VARCHAR(150) NOT NULL
);
-- Inline foreign key declaration
CREATE TABLE orders_inline (
order_id INT PRIMARY KEY AUTO_INCREMENT,
customer_id INT NOT NULL REFERENCES customers(customer_id),
order_date DATE NOT NULL
);
-- Named constraint, easier to reference later (e.g. to drop or alter it)
CREATE TABLE orders_named (
order_id INT PRIMARY KEY AUTO_INCREMENT,
customer_id INT NOT NULL,
order_date DATE NOT NULL,
CONSTRAINT fk_orders_customer
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);
-- Adding a foreign key to an existing table
ALTER TABLE orders_named
ADD CONSTRAINT fk_orders_customer_v2
FOREIGN KEY (customer_id) REFERENCES customers(customer_id);
3. ON DELETE CASCADE: kontrolliertes Mitloeschen
ON DELETE CASCADE weist die Datenbank an, beim Loeschen einer referenzierten Zeile automatisch alle Zeilen mitzuloeschen, die per Fremdschluessel darauf verweisen. Das klassische Beispiel: Loescht man eine Bestellung, sollen alle zugehoerigen Bestellpositionen automatisch mitgeloescht werden, weil eine Bestellposition ohne ihre Bestellung fachlich keinen Sinn ergibt. CASCADE ist damit die richtige Wahl fuer echte Eigentumsbeziehungen, bei denen die abhaengige Zeile ohne die Elternzeile fachlich nicht existieren kann.
CASCADE ist aber auch die gefaehrlichste Option, wenn sie unbedacht eingesetzt wird. Ein CASCADE auf einer Beziehung, die keine echte Eigentumsbeziehung ist, etwa wenn das Loeschen eines Kunden versehentlich saemtliche historischen Bestellungen mitloescht, zerstoert Daten, die aus buchhalterischen oder rechtlichen Gruenden erhalten bleiben muessten. Die Faustregel fuer referentielle Integritaet mit CASCADE: Nur einsetzen, wenn die abhaengige Entitaet wirklich Teil der Elternentitaet ist und ohne sie keine eigenstaendige fachliche Bedeutung hat, niemals bei Beziehungen zwischen unabhaengig existierenden Entitaeten wie Kunde und Bestellung.
-- ON DELETE CASCADE: order lines are true children of the order
CREATE TABLE orders (
order_id INT PRIMARY KEY AUTO_INCREMENT,
customer_id INT NOT NULL,
order_date DATE NOT NULL,
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
-- No CASCADE here: deleting a customer should not silently
-- delete their entire order history
);
CREATE TABLE order_lines (
order_id INT NOT NULL,
product_id INT NOT NULL,
quantity INT NOT NULL,
PRIMARY KEY (order_id, product_id),
FOREIGN KEY (order_id) REFERENCES orders(order_id)
ON DELETE CASCADE -- a line has no meaning without its order
);
-- Deleting an order automatically removes its lines, nothing else
DELETE FROM orders WHERE order_id = 1001;
4. ON DELETE RESTRICT und NO ACTION
ON DELETE RESTRICT, oder gleichbedeutend NO ACTION im SQL-Standard, verhindert das Loeschen einer Zeile, solange noch mindestens eine Zeile darauf per Fremdschluessel verweist. Der Loeschversuch schlaegt mit einem Constraint-Fehler fehl, und die referenzierende Zeile bleibt unveraendert. Das ist das Standardverhalten in den meisten Datenbank-Engines, wenn kein explizites ON DELETE angegeben wird, und es ist die sicherste Option, weil sie niemals stillschweigend Daten veraendert oder loescht.
RESTRICT eignet sich besonders fuer Beziehungen, bei denen ein versehentliches Loeschen katastrophale Folgen haette, etwa das Loeschen eines Produkts, das noch in offenen Bestellungen referenziert wird: FOREIGN KEY (product_id) REFERENCES products(product_id) ON DELETE RESTRICT laesst DELETE FROM products WHERE product_id = 7 mit einem klaren Constraint-Fehler fehlschlagen, sobald order_items diesen Wert noch referenziert, statt die Datenbank still inkonsistent werden zu lassen. Der Fehlerschlag zwingt den aufrufenden Code oder Administrator, die Situation bewusst zu behandeln, etwa indem zuerst die abhaengigen Zeilen archiviert oder umgehaengt werden, statt dass eine Loeschoperation unbemerkt Kaskadeneffekte in der gesamten Datenbank ausloest. Fuer referentielle Integritaet in kritischen Kernentitaeten ist RESTRICT deshalb oft die konservativere und damit sicherere Grundeinstellung.
5. ON DELETE SET NULL und SET DEFAULT
ON DELETE SET NULL setzt die Fremdschluessel-Spalte auf NULL, sobald die referenzierte Zeile geloescht wird, statt die referenzierende Zeile selbst zu loeschen oder das Loeschen zu verhindern. Das eignet sich fuer optionale Beziehungen, bei denen die referenzierende Zeile auch ohne den Bezug sinnvoll weiter existiert, etwa ein Mitarbeiter, dessen zugewiesener Vorgesetzter geloescht wurde: Der Mitarbeiter bleibt bestehen, hat aber vorlaeufig keinen Vorgesetzten mehr zugeordnet. Voraussetzung ist zwingend, dass die Fremdschluesselspalte NULL erlaubt, sonst schlaegt die Constraint bereits beim Anlegen fehl.
ON DELETE SET DEFAULT funktioniert analog, setzt die Spalte aber auf einen vordefinierten Standardwert statt auf NULL, etwa eine "unbekannt"-Kategorie-ID statt einer geloeschten Kategorie. Diese Option wird seltener genutzt als SET NULL, weil sie voraussetzt, dass ein sinnvoller Standardwert tatsaechlich existiert und in der Zieltabelle dauerhaft vorhanden bleibt. Fuer referentielle Integritaet gilt bei beiden Varianten: Sie sind die richtige Wahl fuer optionale, nicht besitzende Beziehungen, waehrend CASCADE fuer echte Eigentumsbeziehungen und RESTRICT fuer geschuetzte Kernentitaeten reserviert bleiben sollte.
| ON DELETE Option | Verhalten | Passt fuer |
|---|---|---|
| CASCADE | Loescht abhaengige Zeilen automatisch mit | Echte Eigentumsbeziehung, z.B. Bestellung und Positionen |
| RESTRICT / NO ACTION | Verhindert das Loeschen mit Fehler | Geschuetzte Kernentitaeten, z.B. referenzierte Produkte |
| SET NULL | Setzt die FK-Spalte auf NULL | Optionale Beziehung, z.B. Vorgesetzter |
| SET DEFAULT | Setzt die FK-Spalte auf einen Standardwert | Kategorien mit dauerhaftem Fallback-Wert |
6. ON UPDATE Verhalten richtig einsetzen
ON UPDATE folgt denselben Optionen wie ON DELETE, CASCADE, RESTRICT, SET NULL und SET DEFAULT, greift aber, wenn sich der Wert der referenzierten Spalte aendert, statt wenn die Zeile geloescht wird. Bei einem Schema mit Surrogate-Primaerschluesseln, die sich per Definition nie aendern, ist ON UPDATE CASCADE meist irrelevant, weil ein Auto-Increment-Wert oder eine UUID ohnehin nie aktualisiert wird. Wird hingegen ein Natural Key als Primaerschluessel genutzt, etwa eine SKU, wird ON UPDATE CASCADE praktisch notwendig, damit eine Umbenennung der SKU automatisch an alle referenzierenden Fremdschluesselspalten weitergereicht wird.
Fehlt ON UPDATE CASCADE in einem solchen Fall, schlaegt jede Aktualisierung des Primaerschluessels mit einem Constraint-Fehler fehl, solange referenzierende Zeilen existieren, was in der Praxis zwingt, entweder die Aenderung ganz zu unterlassen oder erst manuell alle abhaengigen Zeilen zu aktualisieren, bevor die Aenderung am Primaerschluessel moeglich wird. Dieses Verhalten ist ein weiteres starkes Argument dafuer, referentielle Integritaet von Anfang an mit stabilen Surrogate Keys zu kombinieren, wo ON UPDATE Fragen erst gar nicht praktisch relevant werden.
-- ON UPDATE CASCADE only matters when the referenced key can change
-- Relevant with a natural key such as SKU as the primary key
CREATE TABLE products_sku_pk (
sku VARCHAR(50) PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
CREATE TABLE order_items_sku (
order_id INT NOT NULL,
sku VARCHAR(50) NOT NULL,
quantity INT NOT NULL,
PRIMARY KEY (order_id, sku),
FOREIGN KEY (sku) REFERENCES products_sku_pk(sku)
ON UPDATE CASCADE -- SKU rename propagates automatically
ON DELETE RESTRICT -- but deleting a referenced SKU is blocked
);
-- Renaming the SKU now cascades to every order_items row automatically
UPDATE products_sku_pk SET sku = 'NEW-SKU-001' WHERE sku = 'OLD-SKU-001';
7. Warum FK-Checks deaktivieren Daten korrumpiert
Manche Entwickler deaktivieren FK-Checks temporaer, etwa mit SET FOREIGN_KEY_CHECKS=0 in MySQL oder DISABLE TRIGGER ALL in PostgreSQL, um Bulk-Importe zu beschleunigen oder Loeschreihenfolgen zu umgehen, die sonst wegen bestehender Fremdschluessel-Constraints fehlschlagen wuerden. Das Problem: Wird waehrend dieser deaktivierten Phase eine Zeile eingefuegt, die auf eine nicht existierende Elternzeile verweist, oder eine Elternzeile geloescht, obwohl noch Kinder existieren, nimmt die Datenbank das klaglos an. Die referentielle Integritaet ist ab diesem Zeitpunkt gebrochen, ohne dass ein Fehler jemals sichtbar wurde.
Diese Korruption zeigt sich oft erst Wochen oder Monate spaeter, wenn eine Anwendung versucht, ueber einen JOIN die zugehoerige Elternzeile zu laden, und ein leeres Ergebnis erhaelt, wo eigentlich ein Datensatz erwartet wurde. Bis zu diesem Zeitpunkt hat sich die inkonsistente Zeile moeglicherweise laengst weiter vervielfaeltigt, etwa durch Backups, Replikation oder Datenexporte, die die bereits korrupten Daten unveraendert uebernehmen. Die Reparatur eines auf diese Weise entstandenen Datenbestands erfordert oft aufwendige manuelle Analyse, um herauszufinden, welche verwaisten Zeilen ueberhaupt existieren und wie mit ihnen fachlich korrekt umzugehen ist. Wer referentielle Integritaet ernst nimmt, deaktiviert FK-Checks nie fuer den regulaeren Betrieb, sondern hoechstens fuer kontrollierte, sorgfaeltig ueberwachte Einmal-Migrationen, bei denen die Integritaet direkt danach explizit erneut geprueft wird.
-- DANGEROUS pattern: disabling FK checks silently allows orphaned rows
SET FOREIGN_KEY_CHECKS = 0;
DELETE FROM customers WHERE customer_id = 42; -- orders now reference nothing
SET FOREIGN_KEY_CHECKS = 1;
-- No error was raised, but referential integrity is now broken silently
-- SAFE alternative: handle the dependency explicitly, in the right order
DELETE FROM order_lines WHERE order_id IN (
SELECT order_id FROM orders WHERE customer_id = 42
);
DELETE FROM orders WHERE customer_id = 42;
DELETE FROM customers WHERE customer_id = 42;
-- Every step stays covered by active foreign key constraints
-- Detecting existing orphans after a corruption, as a recovery check
SELECT o.order_id FROM orders AS o
LEFT JOIN customers AS c ON c.customer_id = o.customer_id
WHERE c.customer_id IS NULL;
8. Indexierung von Fremdschluesselspalten
Ein oft uebersehener Aspekt bei Fremdschluesseln ist, dass viele Datenbank-Engines, MySQL mit InnoDB eingeschlossen, zwar automatisch einen Index auf der Fremdschluesselspalte anlegen, PostgreSQL hingegen nicht. Ohne einen expliziten Index auf der Fremdschluesselspalte muss jede Loeschung oder Aktualisierung der referenzierten Elternzeile die gesamte Kindtabelle sequenziell durchsuchen, um zu pruefen, ob abhaengige Zeilen existieren, was bei grossen Tabellen erhebliche Performance-Probleme verursacht.
Die Faustregel fuer referentielle Integritaet in der Praxis: Jede Fremdschluesselspalte sollte explizit indexiert werden, unabhaengig davon, ob die verwendete Datenbank-Engine dies automatisch tut oder nicht. Dieser Index beschleunigt nicht nur die referentielle Pruefung bei CASCADE- und RESTRICT-Operationen, sondern auch die weitaus haeufigeren JOIN-Abfragen, die genau diese Fremdschluesselspalte fuer die Verknuepfung nutzen. Ein fehlender Index auf einer stark frequentierten Fremdschluesselspalte ist einer der haeufigsten und am einfachsten behebbaren Performance-Fehler in produktiven Datenbanken.
-- PostgreSQL does NOT auto-index foreign key columns, unlike MySQL/InnoDB
-- Always add the index explicitly for both integrity checks and JOINs
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_id INT NOT NULL,
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);
-- Without this index, DELETE FROM customers and every JOIN scan the table
CREATE INDEX idx_orders_customer_id ON orders (customer_id);
-- Composite foreign keys need a composite index in the same column order
CREATE TABLE shipments (
shipment_id INT PRIMARY KEY,
order_id INT NOT NULL,
product_id INT NOT NULL,
FOREIGN KEY (order_id, product_id) REFERENCES order_lines(order_id, product_id)
);
CREATE INDEX idx_shipments_order_product ON shipments (order_id, product_id);
9. Vendor-Unterschiede: MySQL, PostgreSQL, SQL Server
Alle drei grossen relationalen Datenbanken unterstuetzen die grundlegende FOREIGN KEY-Syntax und alle fuenf ON DELETE / ON UPDATE Optionen weitgehend standardkonform, es gibt aber praxisrelevante Unterschiede. In MySQL erzwingt nur die InnoDB Storage Engine Fremdschluessel-Constraints, die aeltere MyISAM Engine akzeptiert die Syntax zwar, ignoriert die Constraint aber vollstaendig, ein haeufiger Stolperstein bei aelteren oder falsch konfigurierten MySQL-Installationen.
PostgreSQL bietet zusaetzlich DEFERRABLE Constraints, die es erlauben, die Pruefung der referentiellen Integritaet bis zum Ende einer Transaktion aufzuschieben, statt sie bei jedem einzelnen Statement sofort durchzufuehren. Das ist nuetzlich bei zirkulaeren Abhaengigkeiten innerhalb einer einzigen Transaktion, wo eine strikte sofortige Pruefung faelschlich fehlschlagen wuerde, obwohl der Endzustand nach der gesamten Transaktion konsistent ist. SQL Server wiederum verbietet standardmaessig mehrere kaskadierende Pfade, die zum selben Ziel fuehren wuerden, sogenannte "multiple cascade paths", und verlangt in solchen Faellen, einen der Pfade manuell auf NO ACTION zu setzen und stattdessen per Trigger zu loesen. Diese Unterschiede sind selten der Kern einer Architekturentscheidung, sollten aber bekannt sein, bevor ein Schema zwischen Datenbanksystemen portiert wird.
Mironsoft
Datenintegritaet, Schema-Audits und Migrationsberatung
Verwaiste Datensaetze oder deaktivierte FK-Checks im Bestand?
Wir finden verwaiste Zeilen, reparieren gebrochene referentielle Integritaet und entwerfen ein FOREIGN KEY Konzept mit den richtigen CASCADE-, RESTRICT- und SET-NULL-Regeln fuer euer Schema.
Integritaets-Audit
Verwaiste Zeilen und fehlende Constraints systematisch aufspueren
FK-Konzept
CASCADE, RESTRICT und SET NULL fachlich korrekt zuordnen
Performance
Fehlende Indizes auf Fremdschluesselspalten identifizieren
10. Zusammenfassung
Fremdschluessel sind der Mechanismus, mit dem eine Datenbank referentielle Integritaet selbst durchsetzt, unabhaengig davon, ueber welchen Weg Daten geaendert werden. ON DELETE CASCADE gehoert zu echten Eigentumsbeziehungen, RESTRICT schuetzt kritische Kernentitaeten vor versehentlichem Loeschen, SET NULL und SET DEFAULT passen zu optionalen Beziehungen. ON UPDATE CASCADE wird vor allem bei Natural-Key-Primaerschluesseln relevant, die sich aendern koennen, waehrend Surrogate Keys dieses Problem meist von vornherein umgehen.
Das Deaktivieren von FK-Checks fuer vermeintliche Bequemlichkeit ist fast immer ein Fehler, weil es die Datenbank zwingt, inkonsistente Zustaende klaglos zu akzeptieren, deren Reparatur spaeter erheblich teurer ist als die urspruengliche Unbequemlichkeit, die richtige Loeschreihenfolge einzuhalten. Explizite Indizes auf jeder Fremdschluesselspalte sind Pflicht, unabhaengig davon, ob die Datenbank-Engine sie automatisch anlegt, weil sie sowohl Integritaetspruefungen als auch die weitaus haeufigeren JOIN-Abfragen beschleunigen.
Fremdschluessel und referentielle Integritaet: Das Wichtigste auf einen Blick
CASCADE
Nur fuer echte Eigentumsbeziehungen, bei denen das Kind ohne den Elterndatensatz keinen Sinn ergibt.
RESTRICT
Schuetzt kritische Kernentitaeten und erzwingt eine bewusste Behandlung vor dem Loeschen.
FK-Checks nie deaktivieren
Fuehrt zu stillen, oft erst Monate spaeter entdeckten verwaisten Datensaetzen.
Immer indexieren
Jede Fremdschluesselspalte braucht einen expliziten Index, PostgreSQL legt ihn nicht automatisch an.