Constraints in der Data Definition Language (SQL-DDL)

https://bildung.social/@oerinformatik/

https://oer-informatik.de/sql-ddl-constraint

SQL, DDL, Constraints, Primärschlüssel, Fremdschlüssel, Referentielle Integrität, NOT NULL, UNIQUE, CHECK, MariaDB

tl/dr; (ca. 16 min Lesezeit): Die Informationen, ob es sich bei einem Attribut um ein Schlüsselattribut handelt, ob es eingabepflichtig oder einzigartig ist: all das wird über Constraints festgelegt. Es gibt unterschiedliche Möglichkeiten, Constraints festzulegen. Einige werden hier mit Beispielen in MariaDB-Notation vorgestellt – wo sich die DBMS unterscheiden, auch für MS SQL Server, PostgreSQL, Oracle und SQLite: Primär- und Fremdschlüssel (auch zusammengesetzt), NOT NULL, DEFAULT, UNIQUE, CHECK und die Regeln beim Löschen referenzierter Datensätze [AI-Assisted]. (Zuletzt geändert am 28.10.2026)

Dieser Artikel ist Teil einer Artikelreihe über die SQL-DDL: Grundlagen der DDL / Datentypen / Constraints / Übungsaufgaben zur DDL

Schon fit in diesem Thema? Nutze die Kann-Liste und die Leitfragen unter dem Artikel als Checkliste, ob Du die relevanten Kompetenzen bereits hast. Dieser Artikel baut auf der Kann-Liste des Artikels Grundlagen der DDL auf.

Als SQL-Grundlagen kann ich nach Durchlesen des Artikels…

  • … Primärschlüssel aus einer oder mehreren Spalten festlegen und die Anforderungen an einen Primärschlüssel nennen.

  • … Fremdschlüssel anlegen, auch zusammengesetzte, und die Voraussetzungen dafür (gleicher Datentyp, vorhandene Tabelle) erklären.

  • … Spalten mit NOT NULL zu Pflichtfeldern machen und mit DEFAULT Standardwerte vergeben.

  • … mit UNIQUE und CHECK doppelte bzw. unzulässige Werte verhindern und erklären, wie NULL dabei behandelt wird.

  • … die Regeln RESTRICT, CASCADE und SET NULL für ON DELETE/ON UPDATE unterscheiden und begründet auswählen.

  • … Constraints nachträglich mit ALTER TABLE hinzufügen und entfernen.

Darüber hinaus kann ich als SQL-Expertenwissen…

  • … Fehlermeldungen von MariaDB bei verletzten Constraints deuten.

  • … Unterschiede bei automatisch hochgezählten Schlüsseln zwischen den DBMS benennen.

DBMS-einheitliche Constraint-Definitionen

Railroad-Diagramm für CREATE TABLE

Constraints (Nebenbedingungen) sind Regeln, die das DBMS bei jedem INSERT, UPDATE und DELETE prüft. Verletzt eine Änderung eine Regel, lehnt das DBMS sie ab – unabhängig davon, ob die Daten aus einer App, einem Import oder einer Eingabe von Hand stammen. So sichern Constraints die Integrität der Daten.

Sie lassen sich auf zwei Arten notieren: inline direkt hinter der Spalte (nur für Bedingungen, die eine einzige Spalte betreffen) oder als Tabellenbedingung nach den Spalten, dann optional mit einem Namen (CONSTRAINT name …, named constraint). Bedingungen über mehrere Spalten (zusammengesetzte Schlüssel) gehen nur als Tabellenbedingung.

Art der Nebenbedingung Inline Tabellenbedingung / named constraint
Primärschlüssel field INT PRIMARY KEY PRIMARY KEY (field)
zusammengesetzte Primärschlüssel – (nicht möglich) [CONSTRAINT NameDesConstraints] PRIMARY KEY (field1, field2)
Fremdschlüssel FK_Attribut INT REFERENCES Ref_Tabelle(Ref_Attribut)
[ON DELETE …] [ON UPDATE …] 1
[CONSTRAINT NameDesConstraints] FOREIGN KEY (FK_Attribut) REFERENCES Ref_Tabelle(Ref_Attribut)
[ON DELETE …] [ON UPDATE …]
einzigartig UNIQUE [CONSTRAINT NameDesConstraints] UNIQUE (field1[, field2])
eingabepflichtig NOT NULL
optional standardmäßig (explizit in manchen DBMS: NULL)
Eingabe von Default-Werten (z. B. immer 0 bei Zahlen) DEFAULT value
Check-Constraints (nur eingegrenzte Werte erlauben) alter_jahre INT CHECK (alter_jahre >= 18) CONSTRAINT volljaehrig CHECK (alter_jahre >= 18)

Die Constraints PRIMARY KEY, FOREIGN KEY, UNIQUE und CHECK lassen sich in den meisten DBMS als named constraint formulieren. Die zugrunde liegende Syntax findet sich in folgendem Railroad-Diagramm wieder:

Railroad-Diagramm für named constraints

Die Constraints im Einzelnen

Als durchgehendes Beispiel dient ein kleiner Kursanbieter mit Kursen, Teilnehmenden und Buchungen:

CREATE TABLE kurs (
  kurs_id     INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  kursnr      CHAR(8)      NOT NULL UNIQUE,
  titel       VARCHAR(100) NOT NULL,
  preis       DECIMAL(7,2) NOT NULL DEFAULT 0.00,
  max_plaetze TINYINT UNSIGNED NOT NULL,
  CONSTRAINT chk_kurs_preis  CHECK (preis >= 0),
  CONSTRAINT chk_kurs_plaetze CHECK (max_plaetze BETWEEN 1 AND 30)
);

CREATE TABLE teilnehmer (
  teilnehmer_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  nachname      VARCHAR(50)  NOT NULL,
  email         VARCHAR(100) NULL,
  CONSTRAINT uq_teilnehmer_email UNIQUE (email)
);

CREATE TABLE buchung (
  kurs_id       INT UNSIGNED NOT NULL,
  teilnehmer_id INT UNSIGNED NOT NULL,
  gebucht_am    DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
  bezahlt       BOOLEAN      NOT NULL DEFAULT FALSE,
  PRIMARY KEY (kurs_id, teilnehmer_id),
  CONSTRAINT fk_buchung_kurs       FOREIGN KEY (kurs_id)       REFERENCES kurs (kurs_id) ON DELETE CASCADE,
  CONSTRAINT fk_buchung_teilnehmer FOREIGN KEY (teilnehmer_id) REFERENCES teilnehmer (teilnehmer_id)
);

Welche Nebenbedingung an welcher Spalte hängt, zeigt die Übersicht der drei Tabellen:

Tabellen kurs, teilnehmer und buchung mit ihren Constraints: Schlüssel gelb, Pflichtfelder und Eindeutigkeit grün, Standardwerte und Wertebereiche rosa; Fremdschlüssel fk_buchung_kurs mit ON DELETE CASCADE und fk_buchung_teilnehmer mit RESTRICT (mit KI erstellt)

Primärschlüssel (PRIMARY KEY)

Der Primärschlüssel identifiziert jeden Datensatz eindeutig, darf nie NULL sein (MariaDB setzt für Primärschlüsselspalten automatisch NOT NULL) und sollte sich nicht ändern. Jede Tabelle hat höchstens einen Primärschlüssel. Besteht er aus mehreren Spalten wie in buchung, ist erst die Kombination eindeutig: Ein Kurs kann viele Buchungen haben, eine Person viele Kurse buchen – aber dieselbe Person denselben Kurs nur einmal. Ein Primärschlüssel nur aus teilnehmer_id würde dagegen bedeuten, dass jede Person nur einen einzigen Kurs buchen kann – ein typischer Entwurfsfehler.

AUTO_INCREMENT lässt MariaDB den nächsten freien Wert selbst vergeben (künstlicher Schlüssel, Surrogatschlüssel). Die Spalte muss ein Schlüssel sein; beim INSERT lässt man sie einfach weg. Die anderen DBMS haben dafür eigene Schreibweisen, hier am Beispiel der Tabelle teilnehmer:

CREATE TABLE teilnehmer (
  teilnehmer_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  nachname      VARCHAR(50)  NOT NULL,
  email         VARCHAR(100) NULL,
  CONSTRAINT uq_teilnehmer_email UNIQUE (email)
);

Pflichtfeld (NOT NULL) und Standardwert (DEFAULT)

NOT NULL verlangt bei jedem Datensatz einen Wert. Ohne Angabe ist eine Spalte optional (NULL). DEFAULT legt fest, welcher Wert gespeichert wird, wenn beim INSERT keiner angegeben ist – etwa DEFAULT CURRENT_TIMESTAMP für den Buchungszeitpunkt oder DEFAULT FALSE für „noch nicht bezahlt“. Der Standardwert muss zum Datentyp passen: BOOLEAN NOT NULL DEFAULT 'nein' lehnt MariaDB ab (Fehler 1067 „Invalid default value“). Pflichtfeld und Standardwert lassen sich kombinieren: NOT NULL DEFAULT 0.00 erlaubt kein NULL, ergänzt aber fehlende Angaben.

Eindeutigkeit (UNIQUE)

UNIQUE verhindert doppelte Werte in einer Spalte oder – als Tabellenbedingung – doppelte Wertekombinationen. Anders als beim Primärschlüssel darf es mehrere UNIQUE-Bedingungen je Tabelle geben, und NULL ist erlaubt, sogar mehrfach: Zwei Teilnehmende ohne E-Mail-Adresse verletzen uq_teilnehmer_email nicht. (Ausnahme: MS SQL Server lässt in einer UNIQUE-Spalte nur ein einziges NULL zu.) Typische Kandidaten sind natürliche Schlüssel neben einem künstlichen Primärschlüssel, etwa kursnr, E-Mail-Adressen oder Rechnungsnummern. MariaDB legt für jede UNIQUE-Bedingung automatisch einen Index an.

Wertebereich (CHECK)

CHECK (bedingung) erlaubt nur Werte, für die die Bedingung nicht FALSE ergibt. Ist ein beteiligter Wert NULL, ist die Bedingung „unbekannt“ und gilt als erfüllt – wer NULL ausschließen will, braucht zusätzlich NOT NULL. Bedingungen dürfen mehrere Spalten derselben Zeile vergleichen (CHECK (ende > beginn)), aber keine anderen Zeilen oder Tabellen. MariaDB prüft CHECK-Bedingungen seit Version 10.2, MySQL erst seit 8.0.16 – ältere Versionen haben sie akzeptiert, aber ignoriert.

Fremdschlüssel (FOREIGN KEY … REFERENCES)

Ein Fremdschlüssel sichert die referentielle Integrität: In buchung.kurs_id dürfen nur Werte stehen, die es in kurs.kurs_id gibt (oder NULL, wenn die Spalte optional ist). Damit MariaDB ihn anlegt, müssen

  • die referenzierte Tabelle schon existieren (Reihenfolge beim Anlegen!),

  • die referenzierten Spalten Primärschlüssel oder UNIQUE sein,

  • beide Spalten denselben Datentyp haben, bei Ganzzahlen einschließlich UNSIGNED,

  • beide Tabellen die Speicher-Engine InnoDB nutzen (Standard).

Sonst meldet MariaDB Fehler 1005 mit „errno: 150 Foreign key constraint is incorrectly formed“. Für die Fremdschlüsselspalte legt InnoDB automatisch einen Index an, falls es noch keinen passenden gibt.

Besteht der Primärschlüssel der referenzierten Tabelle aus mehreren Spalten, braucht auch der Fremdschlüssel alle diese Spalten in derselben Reihenfolge:

CREATE TABLE zertifikat (
  zertifikat_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  kurs_id       INT UNSIGNED NOT NULL,
  teilnehmer_id INT UNSIGNED NOT NULL,
  ausgestellt_am DATE NOT NULL,
  CONSTRAINT fk_zert_buchung FOREIGN KEY (kurs_id, teilnehmer_id)
    REFERENCES buchung (kurs_id, teilnehmer_id)
);

So kann ein Zertifikat nur für eine Person ausgestellt werden, die genau diesen Kurs gebucht hat – zwei getrennte Fremdschlüssel auf kurs und teilnehmer würden das nicht sicherstellen. Ist ein Teil eines zusammengesetzten Fremdschlüssels NULL, prüft MariaDB ihn nicht; die Spalten sollten deshalb NOT NULL sein.

Was passiert beim Löschen? ON DELETE und ON UPDATE

Wird ein Datensatz gelöscht (oder sein Schlüssel geändert), auf den noch Fremdschlüssel verweisen, entscheidet die Regel am Fremdschlüssel:

Regeln für ON DELETE und ON UPDATE
Regel Wirkung beim Löschen des referenzierten Datensatzes Beispiel
RESTRICT / NO ACTION (Standard, wenn nichts angegeben ist) Löschen wird abgelehnt (Fehler 1451) Teilnehmende mit Buchungen können nicht gelöscht werden
CASCADE abhängige Datensätze werden mitgelöscht Wird ein Kurs gelöscht, verschwinden seine Buchungen
SET NULL Fremdschlüssel wird auf NULL gesetzt (Spalte muss NULL erlauben) Wird ein Raum gelöscht, bleibt der Kurs erhalten, raum_id wird NULL (siehe fk_kurs_raum unten)

CASCADE passt, wenn die abhängigen Datensätze ohne den Elterndatensatz sinnlos sind (Positionen eines Auftrags, Buchungen eines abgesagten Kurses). Bei eigenständigen Stammdaten oder Daten, die aufbewahrt werden müssen (Rechnungen), ist RESTRICT sicherer. ON UPDATE CASCADE überträgt geänderte Schlüsselwerte – bei künstlichen Schlüsseln, die sich nie ändern, ist das selten nötig.

Typische Fehlermeldungen

Fehlermeldungen von MariaDB bei verletzten Constraints
Fehler Meldung (gekürzt) Ursache
1062 Duplicate entry … for key … doppelter Wert bei PRIMARY KEY oder UNIQUE
1048 Column … cannot be null NULL in einer NOT NULL-Spalte
4025 CONSTRAINT … failed CHECK-Bedingung verletzt
1452 Cannot add or update a child row: a foreign key constraint fails Fremdschlüsselwert ohne passenden Datensatz
1451 Cannot delete or update a parent row: a foreign key constraint fails referenzierter Datensatz soll gelöscht werden (RESTRICT)
1005 (errno 150) Foreign key constraint is incorrectly formed Typen passen nicht, Tabelle oder Index fehlt

DBMS-spezifische Constraint-Notationen

Eigenschaft MSSQL MySQL / MariaDB postgreSQL OracleDB SQLite
Automatisch hochgezählte Schlüsselwerte IDENTITY AUTO_INCREMENT SERIAL bzw. GENERATED … AS IDENTITY GENERATED … AS IDENTITY (ab 12c) AUTOINCREMENT bzw. INTEGER PRIMARY KEY
offizielle Dokumentation des DBMS zu Constraints SQL-Server-Doku MySQL Constraints MariaDB Constraints PostgreSQL-Doku Oracle-Doku SQLite-Doku

Nachträgliches Ergänzen von Constraints

Einen Constraint im Nachhinein anfügen (als named constraint):

ALTER TABLE personen ADD CONSTRAINT volljaehrig  CHECK (alter_jahre >= 18);

Genauso lassen sich Primärschlüssel, UNIQUE-Bedingungen und Fremdschlüssel ergänzen, etwa um eine neue Tabelle einzubinden:

ALTER TABLE kurs
  ADD CONSTRAINT uq_kurs_titel UNIQUE (titel);

CREATE TABLE raum (
  raum_id     INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  bezeichnung VARCHAR(40)  NOT NULL UNIQUE
);

ALTER TABLE kurs
  ADD COLUMN raum_id INT UNSIGNED NULL,
  ADD CONSTRAINT fk_kurs_raum FOREIGN KEY (raum_id) REFERENCES raum (raum_id) ON DELETE SET NULL;

Beim nachträglichen Hinzufügen prüft MariaDB alle vorhandenen Datensätze: Gibt es schon doppelte Werte, Werte außerhalb der CHECK-Bedingung oder Fremdschlüssel ohne Partner, scheitert die Anweisung. Das gilt auch für NOT NULL, das über MODIFY gesetzt wird: Erst die fehlenden Werte ergänzen, dann die Spalte zum Pflichtfeld machen (die Schreibweisen der anderen DBMS zeigt der Artikel Grundlagen der DDL).

UPDATE teilnehmer SET email = 'unbekannt@example.org' WHERE email IS NULL;   -- Achtung: kollidiert mit UNIQUE, sobald zwei Zeilen betroffen sind
ALTER TABLE teilnehmer MODIFY COLUMN email VARCHAR(100) NOT NULL;

Das Beispiel zeigt eine typische Falle: Ein fester Platzhalter verletzt bei mehreren betroffenen Zeilen die UNIQUE-Bedingung. Dann braucht jede Zeile einen eigenen Wert (z. B. CONCAT('unbekannt-', teilnehmer_id, '@example.org')).

Named Constraints im Nachhinein löschen:

ALTER TABLE personen DROP CONSTRAINT volljaehrig;          -- CHECK
ALTER TABLE buchung DROP FOREIGN KEY fk_buchung_kurs;      -- Fremdschlüssel
ALTER TABLE teilnehmer DROP INDEX uq_teilnehmer_email;     -- UNIQUE (über seinen Index)

Neuere MariaDB-Versionen akzeptieren für alle benannten Constraints auch DROP CONSTRAINT. Welche Constraints eine Tabelle hat, zeigt SHOW CREATE TABLE tabellenname;.

Allgemeine Infos zum Tabellenerstellen

Um Tabellen passgenau erstellen zu können, benötigen wir Wissen über die verfügbaren Grundlagen zum Tabellen- und Datenbankerstellen, die Datentypen sowie die Constraints (wie Schlüsselattribute, Eingabepflicht usw.).

Leitfragen

  1. Welche Anforderungen muss ein Primärschlüssel erfüllen, und wann ist er zusammengesetzt?

Ein Primärschlüssel identifiziert jeden Datensatz eindeutig, darf nie NULL sein und sollte sich nicht ändern; jede Tabelle hat höchstens einen. Zusammengesetzt ist er, wenn erst die Kombination mehrerer Spalten eindeutig ist, etwa PRIMARY KEY (kurs_id, teilnehmer_id) in einer Buchungstabelle, in der eine Person viele Kurse, aber jeden nur einmal buchen kann; das geht nur als Tabellenbedingung – siehe Abschnitt „Primärschlüssel (PRIMARY KEY)“.

  1. Worin unterscheiden sich PRIMARY KEY und UNIQUE, insbesondere im Umgang mit NULL?

Beide verhindern doppelte Werte, aber es gibt nur einen PRIMARY KEY je Tabelle und beliebig viele UNIQUE-Bedingungen. Der Primärschlüssel darf nie NULL sein, während eine UNIQUE-Spalte NULL erlaubt, sogar mehrfach – zwei Teilnehmende ohne E-Mail-Adresse verletzen die Bedingung nicht – siehe Abschnitt „Eindeutigkeit (UNIQUE)“.

  1. Was bewirken NOT NULL und DEFAULT, einzeln und kombiniert?

NOT NULL macht eine Spalte zum Pflichtfeld, das bei jedem Datensatz einen Wert verlangt. DEFAULT legt den Wert fest, der gespeichert wird, wenn beim INSERT keiner angegeben ist, und muss zum Datentyp passen. Kombiniert (z. B. NOT NULL DEFAULT 0.00) ist NULL verboten, fehlende Angaben werden aber automatisch ergänzt – siehe Abschnitt „Pflichtfeld (NOT NULL) und Standardwert (DEFAULT)“.

  1. Wie formulierst Du eine CHECK-Bedingung, und was geschieht, wenn ein beteiligter Wert NULL ist?

Du schreibst CHECK (bedingung) inline an die Spalte oder als benannte Tabellenbedingung, z. B. CONSTRAINT chk_kurs_preis CHECK (preis >= 0); die Bedingung darf mehrere Spalten derselben Zeile vergleichen, aber keine anderen Zeilen oder Tabellen. Ist ein beteiligter Wert NULL, ist die Bedingung „unbekannt“ und gilt als erfüllt – um NULL auszuschließen, brauchst Du zusätzlich NOT NULL – siehe Abschnitt „Wertebereich (CHECK)“.

  1. Welche Voraussetzungen müssen erfüllt sein, damit MariaDB einen Fremdschlüssel anlegt?

Die referenzierte Tabelle muss bereits existieren, die referenzierten Spalten müssen Primärschlüssel oder UNIQUE sein, beide Spalten müssen denselben Datentyp haben (bei Ganzzahlen einschließlich UNSIGNED), und beide Tabellen müssen InnoDB nutzen. Sonst meldet MariaDB Fehler 1005 mit „errno: 150“ – siehe Abschnitt „Fremdschlüssel (FOREIGN KEY … REFERENCES)“.

  1. Wann wählst Du beim Fremdschlüssel CASCADE, wann RESTRICT, wann SET NULL?

CASCADE wählst Du, wenn die abhängigen Datensätze ohne den Elterndatensatz sinnlos sind (z. B. Buchungen eines gelöschten Kurses). RESTRICT (Standard) ist sicherer bei eigenständigen Stammdaten oder Daten, die aufbewahrt werden müssen, etwa Rechnungen. SET NULL passt, wenn der abhängige Datensatz erhalten bleiben und nur seine Zuordnung verlieren soll; die Spalte muss dann NULL erlauben – siehe Abschnitt „Was passiert beim Löschen? ON DELETE und ON UPDATE“.

  1. Wie fügst Du einer gefüllten Tabelle nachträglich einen Fremdschlüssel oder eine Pflichtspalte hinzu?

Einen Fremdschlüssel ergänzt Du mit ALTER TABLE … ADD CONSTRAINT name FOREIGN KEY (…) REFERENCES …; MariaDB prüft dabei alle vorhandenen Datensätze, Werte ohne Partner lassen die Anweisung scheitern. Für eine Pflichtspalte füllst Du zuerst die fehlenden Werte per UPDATE (bei UNIQUE mit eindeutigen Werten je Zeile) und machst die Spalte danach mit ALTER TABLE … MODIFY COLUMN … NOT NULL zum Pflichtfeld – siehe Abschnitt „Nachträgliches Ergänzen von Constraints“.


Hinweis zur Nachnutzung als Open Educational Resource (OER)

Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: “Constraints in der Data Definition Language (SQL-DDL)” von oer-informatik.de (H. Stein), Lizenz: CC BY 4.0. Der Artikel wurde unter https://oer-informatik.de/sql-ddl-constraint veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/db-sql/sql. Erstellt mit KI-Assistenz; von mir umfassend redaktionell überarbeitet, geprüft und freigegeben. Stand: 28.10.2026.

[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]


  1. Die Kurzform fk_attribut INT REFERENCES ref_tabelle (ref_attribut) direkt an der Spalte wertet MariaDB erst ab Version 10.5 aus; ältere MariaDB- und MySQL-Versionen ignorieren sie stillschweigend. Sicherer ist die Schreibweise mit FOREIGN KEY.↩︎

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: