Data Definition Language (SQL-DDL)
https://bildung.social/@oerinformatik/
https://oer-informatik.de/sql-ddl-basics
SQL, DDL, Data Definition Language, CREATE TABLE, ALTER TABLE, DROP TABLE, TRUNCATE, Fremdschlüssel, Index, MariaDB
tl/dr; (ca. 24 min Lesezeit): SQL besteht aus mehreren Teilsprachen, die sich mit der Datenbankstruktur (DDL), dem Datenbankinhalt (DML) und den Berechtigungen (DCL) beschäftigen. In diesem Artikel soll es zunächst um die Grundlagen der Struktur gehen, die in der Data Definition Language (SQL-DDL) beschrieben wird. Du lernst, Datenbanken und Tabellen anzulegen, Tabellen nachträglich umzubauen, sie zu löschen und Indizes für schnelle Abfragen zu setzen – in MariaDB/MySQL und, wo sich die Befehle unterscheiden, auch in MS SQL Server, PostgreSQL, Oracle und SQLite [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 Der logische Datenbankentwurf mit dem Relationenmodell auf.
Als SQL-Grundlagen kann ich nach Durchlesen des Artikels…
… die Teilsprachen DDL, DML, DQL und DCL unterscheiden und Befehle zuordnen.
… aus einem Relationenmodell
CREATE TABLE-Anweisungen mit Datentypen, Primär- und Fremdschlüsseln erstellen.… begründen, in welcher Reihenfolge Tabellen mit Fremdschlüsseln angelegt und gelöscht werden müssen.
… Tabellen mit
ALTER TABLEändern: Spalten hinzufügen, ändern, umbenennen und entfernen sowie Tabellen nachträglich per Fremdschlüssel einbinden.… eine vorhandene Spalte zum Pflichtfeld machen, nachdem fehlende Werte ergänzt wurden.
… Tabellen mit
DROP TABLElöschen und den Unterschied zuTRUNCATEundDELETEerläutern.… beschreiben, wozu Spalten indiziert werden, Indizes anlegen und Nachteile von Indizes nennen.
Darüber hinaus kann ich als SQL-Expertenwissen…
… eine Spalte mit Zeichenkettenfunktionen auf mehrere Spalten aufteilen.
… Archivtabellen mit
CREATE TABLE … LIKEoderCREATE TABLE … AS SELECTanlegen und die Unterschiede nennen.… zusammengesetzte Indizes sinnvoll anordnen und erklären, warum DDL-Anweisungen in MariaDB/MySQL nicht mit
ROLLBACKzurückgenommen werden können.
Einstieg in die Data Definition Language (DDL)
Struktureinheiten in der DDL
Die meisten Datenbankmanagementsysteme gliedern die Daten in unterschiedlichen Strukturen:
Datenbanken sind in der Regel die oberste Struktur, in der alles Weitere organisiert ist.
Schemata sind Einheiten, die mehrere Tabellen zusammenfassen. Sie bilden Namensräume, innerhalb derer Bezeichner eindeutig sind. Nicht alle Datenbankmanagementsysteme arbeiten mit Schemata.
Tabellen schließlich sind die Struktur, die die eigentliche Datenmenge speichert (die Datensätze, die wir in Entitätstypen und Relationen gegliedert hatten).
Für jede dieser Einheiten stellt die DDL Befehle zum Erstellen, Lesen, Ändern und Löschen bereit. Diese Abfolge (Create, Read, Update, Delete – kurz CRUD) wird uns im Verlauf der Datenbankarbeit noch häufiger begegnen.
Die Grafik zeigt die Struktureinheiten am Beispiel eines Geräteverleihs, der weiter unten angelegt wird, mit den zugehörigen DDL-Befehlen:
Die DDL ist eine von vier Teilsprachen von SQL. Sie verändert die Struktur der Datenbank, die übrigen arbeiten mit dem Inhalt bzw. den Rechten:
| Teilsprache | Befehle | wirkt auf |
|---|---|---|
| DDL (Data Definition Language) | CREATE, ALTER, DROP, TRUNCATE, RENAME |
Struktur: Datenbanken, Tabellen, Spalten, Indizes |
| DML (Data Manipulation Language) | INSERT, UPDATE, DELETE |
Inhalt: Datensätze |
| DQL (Data Query Language) | SELECT |
Inhalt lesen (oft zur DML gezählt) |
| DCL (Data Control Language) | GRANT, REVOKE, CREATE USER |
Berechtigungen |
Wichtig für die Praxis: DDL-Anweisungen lösen in MariaDB und MySQL ein implizites COMMIT aus. Eine laufende Transaktion wird dabei festgeschrieben, und die DDL-Anweisung selbst lässt sich nicht mit ROLLBACK zurücknehmen. Wer eine Spalte oder Tabelle löscht, sollte deshalb vorher eine Sicherung haben. Oracle verhält sich ebenso; PostgreSQL, SQLite und MS SQL Server können DDL-Anweisungen dagegen innerhalb einer Transaktion zurückrollen.
Die Sache mit dem Standard und der Realität…
SQL ist ein offizieller ISO/IEC-Standard, der die Grammatik von SQL-Statements festlegen soll (aktuell: ISO/IEC 9075:2023, „SQL:2023“ 1). Eine frühe Version dieses Standards, SQL-92, bildet die Basis der Abfragesprachen aller relationalen Datenbankmanagementsysteme. Über diese Basis hinaus hat jedes System seine eigenen Sprachbestandteile entwickelt, die in der Struktur ähnlich, in der konkreten Ausprägung jedoch unterschiedlich sind. Es gibt große Überschneidungen bei trivialen SQL-Abfragen, aber im Detail unterscheiden sich die SQL-Implementierungen erheblich.
Ich gehe deshalb immer wieder auf Implementierungseigenheiten ein: unterschiedliche Datentypen, Bezeichnungen und Befehle. Im Blick sind die verbreitetsten Datenbankmanagementsysteme MySQL/MariaDB, MS SQL Server (T-SQL), PostgreSQL, Oracle und SQLite. Wo sich ihre Befehle unterscheiden, zeigen Tabs die jeweilige Schreibweise (voreingestellt ist MariaDB/MySQL, mit „all“ siehst Du alle Varianten untereinander); kurze Befehle stehen in Tabellen nebeneinander.
Datenbanken verwalten
Die übergeordnete Struktureinheit in DBMS sind Datenbanken. In der Regel ist es sinnvoll, nicht die Standarddatenbank des DBMS für eigene Tabellen zu nutzen - daher muss eine eigene Datenbank erzeugt und verwaltet werden.
Datenbank erzeugen
Die Erzeugung von Datenbanken erfolgt in den meisten DBMS noch sehr nah am Standard.
CREATE DATABASE datenbankname;Wenn sie nur erzeugt werden soll, falls sie nicht bereits existiert:
CREATE DATABASE IF NOT EXISTS datenbankname;Datenbank auswählen
USE datenbankname;Für den Betrieb wird zur Datenbank meist ein eigener Benutzer angelegt, der nur die nötigen Rechte hat – das gehört zur DCL: CREATE USER 'app'@'10.0.0.%' IDENTIFIED BY '…'; und GRANT SELECT, INSERT ON datenbankname.* TO 'app'@'10.0.0.%';. Mehr dazu im Artikel SQL-DCL: Benutzer und Rechte.
Vorhandene Datenbanken anzeigen
| MSSQL | MySQL / MariaDB | postgreSQL | OracleDB | SQLite | |
|---|---|---|---|---|---|
SELECT name FROM sys.databases; |
SHOW DATABASES; |
SELECT datname FROM pg_database;in psql: \l |
SELECT name FROM v$database;Pluggable Databases: SELECT name FROM v$pdbs; |
PRAGMA database_list;in der sqlite3-Shell: .databases |
Datenbanken anpassen
Die meisten Eigenschaften einer Datenbank lassen sich auch im Nachgang noch anpassen. Das Schlüsselwort dazu lautet in der Regel ALTER DATABASE. Beim Umbenennen unterscheiden sich die DBMS deutlich:
MariaDB und MySQL können eine Datenbank nicht umbenennen. Man legt eine neue Datenbank an und verschiebt die Tabellen:
CREATE DATABASE neuedb;
RENAME TABLE altedb.tabelle TO neuedb.tabelle;Zeichensatz und Sortierung einer Datenbank ändert:
ALTER DATABASE dietestdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;MariaDB und MySQL verfügen über mehrere Speicher-Engines (storage engines) – früher war MyISAM voreingestellt, heute ist es in der Regel InnoDB. Per DDL-Befehl kann zwischen unterschiedlichen Engines gewechselt werden:
ALTER TABLE `dietestdb`.`mp3sammlung` ENGINE = InnoDB;Datenbank löschen
DROP DATABASE IF EXISTS datenbankname;IF EXISTS verhindert einen Fehler, wenn die Datenbank nicht existiert. DROP SCHEMA ist in MariaDB/MySQL gleichbedeutend.
Namensräume in Schema verwalten
Nicht jedes DBMS nutzt Schemata zur Gliederung von Namensräumen. Einige nutzen sie, ohne dass man es merkt. Bei anderen wundert man sich, dass in jedem Befehl eine vermeintlich nutzlose Zeichenkette mitgegeben wird. So steht in MS SQL Server beispielsweise oft das Standardschema dbo vor dem Tabellennamen (dbo.kunden).
Neues Schema erzeugen
MariaDB verwendet keine eigenen Schemata und nutzt die Begriffe DATABASE und SCHEMA in Befehlen synonym. Die folgende Anweisung legt also eine Datenbank an:
CREATE SCHEMA IF NOT EXISTS meinSchemaName;Vorhandene Schema anzeigen
| MSSQL | MySQL / MariaDB | postgreSQL | OracleDB | SQLite | |
|---|---|---|---|---|---|
SELECT name FROM sys.schemas; |
SHOW SCHEMAS; (gleichbedeutend mit SHOW DATABASES;) |
SELECT schema_name FROM information_schema.schemata;in psql: \dn |
SELECT username FROM all_users; |
PRAGMA database_list; |
Tabellen bearbeiten
Namensgebung
Die einzelnen DBMS haben jeweils eigene Namensregeln. Eine sinnvolle Namensregel lautet etwa:
Erstes Zeichen muss ein Buchstabe sein, danach bis zu 29 Zeichen, Groß- oder Kleinbuchstaben, Ziffern, Unterstrich, Dollar oder Raute, als regulärer Ausdruck:
[A-Za-z][A-Za-z0-9_$#]{0,29}.Ob Tabellennamen zwischen Groß- und Kleinschreibung unterscheiden (case sensitive), hängt vom DBMS und vom Betriebssystem ab: MariaDB und MySQL legen Tabellen als Dateien ab, unter Linux sind
Kundeundkundedaher zwei verschiedene Tabellen, unter Windows nicht. Daher: besser nur Kleinbuchstaben verwenden.Tabellennamen sollten einheitlich im Singular (wie
mitgliedundgeraetin den Beispielen unten) oder einheitlich im Plural gehalten sein (Konvention, nicht Dogma).Keine Bindestriche, Leerzeichen oder Umlaute in Namen:
betrieb-ansprechpersonliest MariaDB als Rechnung „betrieb minus ansprechperson“ und meldet einen Syntaxfehler. Besser ist ein Unterstrich (betrieb_ansprechperson); notfalls lassen sich solche Namen in Backticks setzen (`betrieb-ansprechperson`), müssen dann aber in jeder Abfrage so geschrieben werden.
Tabellen erzeugen
CREATE TABLE IF NOT EXISTS datenbankname.tabellenname (attribut1 datentyp1, attribut2 datentyp2, ...);IF NOT EXISTS kennen MariaDB/MySQL, PostgreSQL, SQLite und Oracle ab 23ai, MS SQL Server nicht.
Neben den Datentypen (siehe Datentyp-Übersicht) können beim Erzeugen / Anpassen von Tabellen auch Constraints festgelegt werden. Details finden sich auf der gesonderten Übersicht zu Constraints.
Vom Relationenmodell zur Tabelle
Ein Beispiel: Ein Verein verleiht Geräte an seine Mitglieder. Das Relationenmodell sieht so aus (Primärschlüssel unterstrichen, Fremdschlüssel mit #):
mitglied (mitglied_id, vorname, nachname, email, eintritt_am)
geraet (geraet_id, bezeichnung, kaution)
ausleihe (#geraet_id, #mitglied_id, von, bis)
Jede Relation wird zu einer CREATE TABLE-Anweisung. Zu jedem Attribut gehören ein Datentyp und – je nach Anforderung – Nebenbedingungen (Constraints) wie Pflichtfeld oder Eindeutigkeit:
CREATE TABLE mitglied (
mitglied_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
vorname VARCHAR(50) NOT NULL,
nachname VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
eintritt_am DATE NOT NULL
);
CREATE TABLE geraet (
geraet_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
bezeichnung VARCHAR(80) NOT NULL,
kaution DECIMAL(6,2) NOT NULL DEFAULT 0.00,
CONSTRAINT chk_geraet_kaution CHECK (kaution >= 0)
);
CREATE TABLE ausleihe (
geraet_id INT UNSIGNED NOT NULL,
mitglied_id INT UNSIGNED NOT NULL,
von DATE NOT NULL,
bis DATE NULL,
PRIMARY KEY (geraet_id, mitglied_id, von),
CONSTRAINT fk_ausleihe_geraet FOREIGN KEY (geraet_id) REFERENCES geraet (geraet_id),
CONSTRAINT fk_ausleihe_mitglied FOREIGN KEY (mitglied_id) REFERENCES mitglied (mitglied_id)
);Dabei gilt:
Spaltendefinitionen und Tabellenbedingungen werden durch Kommas getrennt; nach dem letzten Eintrag steht kein Komma, nach der schließenden Klammer ein Semikolon.
Ein Primärschlüssel aus einer Spalte kann direkt an der Spalte stehen (
… PRIMARY KEY), ein zusammengesetzter Primärschlüssel nur als eigene Zeile (PRIMARY KEY (geraet_id, mitglied_id, von)).Eine Fremdschlüsselspalte muss denselben Datentyp haben wie der Primärschlüssel, auf den sie verweist – einschließlich
UNSIGNED. Ihr Name darf abweichen (z. B.annahme_mitarbeiter_id), entscheidend istREFERENCES.Spaltennamen werden in
CREATE TABLEohne Tabellennamen geschrieben (email, nichtmitglied.email).Welche Datentypen es gibt und welche Nebenbedingungen möglich sind, beschreiben die Artikel zu Datentypen und Constraints.
Die Reihenfolge zählt
Ein Fremdschlüssel kann nur auf eine Tabelle verweisen, die es schon gibt. Deshalb werden zuerst die Tabellen ohne Fremdschlüssel angelegt (mitglied, geraet), dann die Tabellen, die auf sie verweisen (ausleihe). Beim Löschen ist es umgekehrt: Solange ein Fremdschlüssel auf eine Tabelle zeigt, verweigert MariaDB DROP TABLE (Fehler 1451 „Cannot delete or update a parent row: a foreign key constraint fails“) – auch wenn die Tabellen leer sind.
Welche Struktur eine Tabelle hat, zeigen DESCRIBE tabellenname; (Spalten) und SHOW CREATE TABLE tabellenname; (vollständige Anweisung einschließlich Constraints und Indizes).
Tabellen nach Vorlage anlegen
Für Archiv- oder Testtabellen gibt es zwei Abkürzungen:
CREATE TABLE mitglied_archiv LIKE mitglied;
CREATE TABLE ehemalige AS
SELECT mitglied_id, nachname, eintritt_am FROM mitglied WHERE eintritt_am < '2020-01-01';CREATE TABLE … LIKElegt eine leere Tabelle mit derselben Struktur an: Spalten, Datentypen, Standardwerte und Indizes, aber keine Fremdschlüssel.CREATE TABLE … AS SELECTlegt eine Tabelle aus einem Abfrageergebnis an und übernimmt die Daten, aber nur die Spalten mit ihren Datentypen – keinen Primärschlüssel, keineUNIQUE-Bedingungen und keine Fremdschlüssel.Soll eine Archivtabelle nur einen Teil der Spalten enthalten und trotzdem einen Primärschlüssel haben, legt man sie mit
CREATE TABLEselbst an und füllt sie mitINSERT INTO … SELECT …(siehe Datensätze einfügen).
Tabellenstruktur anpassen
ALTER TABLE ...Tabellen verändern:
ALTER TABLE tabellenName ADD neuesAttribut VARCHAR(50);In MySQL und MariaDB kann zusätzlich die Position des neuen Attributs bestimmt werden, indem AFTER vorgaengerAttribut (oder FIRST) ergänzt wird. Das Schlüsselwort COLUMN ist optional (ADD COLUMN …), mehrere Änderungen werden durch Kommas getrennt:
ALTER TABLE mitglied
ADD COLUMN telefon VARCHAR(25) NULL AFTER email,
ADD COLUMN aktiv BOOLEAN NOT NULL DEFAULT TRUE;Vorhandene Datensätze erhalten in der neuen Spalte den Standardwert (DEFAULT), ohne Standardwert NULL. Ist die neue Spalte NOT NULL ohne DEFAULT, trägt MariaDB den „impliziten Standardwert“ des Datentyps ein (0, Leerstring) – das ist selten gewollt.
Warum ALTER TABLE und nicht CREATE TABLE? Eine vorhandene Tabelle lässt sich nicht ein zweites Mal anlegen (Fehler 1050 „Table already exists“). Sie vorher mit DROP TABLE zu löschen, würde alle Datensätze vernichten und scheitert ohnehin, solange andere Tabellen per Fremdschlüssel auf sie verweisen. ALTER TABLE ändert dagegen nur die Struktur: Die vorhandenen Datensätze bleiben erhalten, neue Spalten müssen bei Bedarf noch per UPDATE gefüllt werden.
Datentyp eines Merkmals ändern:
ALTER TABLE tabellenName MODIFY attributName VARCHAR(128);Bei MODIFY wird die vollständige Spaltendefinition angegeben. Was fehlt, geht verloren: Stand vorher NOT NULL DEFAULT 'x' und schreibt man nur MODIFY attributName VARCHAR(128), ist die Spalte danach optional und ohne Standardwert.
Eine Spalte zum Pflichtfeld machen: Haben bereits Datensätze keinen Wert, lehnt das DBMS NOT NULL ab (MariaDB: Fehler 1138 „Invalid use of NULL value“). Deshalb zuerst die Lücken füllen, dann die Spalte ändern:
UPDATE mitglied SET telefon = 'unbekannt' WHERE telefon IS NULL;
ALTER TABLE mitglied MODIFY COLUMN telefon VARCHAR(25) NOT NULL;Merkmale umbenennen:
ALTER TABLE tabellenName CHANGE alterAttributName neuerAttributName VARCHAR(128);CHANGE verlangt wie MODIFY die vollständige Spaltendefinition. Seit MariaDB 10.5 und MySQL 8.0 geht es auch ohne Wiederholung des Datentyps:
ALTER TABLE tabellenName RENAME COLUMN alterAttributName TO neuerAttributName;Merkmale löschen:
ALTER TABLE tabellenName DROP COLUMN zuLoeschendesAttribut;Diese Schreibweise verstehen alle hier betrachteten DBMS (SQLite ab Version 3.35); in MariaDB/MySQL, PostgreSQL und SQLite darf COLUMN auch fehlen.
Tabellen nachträglich verknüpfen (eine neue Tabelle „einbinden“):
CREATE TABLE abteilung (
abteilung_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
bezeichnung VARCHAR(40) NOT NULL UNIQUE
);
ALTER TABLE mitglied
ADD COLUMN abteilung_id INT UNSIGNED NULL,
ADD CONSTRAINT fk_mitglied_abteilung FOREIGN KEY (abteilung_id)
REFERENCES abteilung (abteilung_id) ON DELETE SET NULL;Die neue Fremdschlüsselspalte ist zunächst optional, weil die vorhandenen Mitglieder noch keiner Abteilung zugeordnet sind. Constraints lassen sich auch wieder entfernen, in MariaDB etwa mit ALTER TABLE mitglied DROP FOREIGN KEY fk_mitglied_abteilung;. Wie Constraints in den anderen DBMS nachträglich hinzugefügt und gelöscht werden, zeigt der Artikel zu Constraints.
Beispiel: Eine Spalte aufteilen
Eine Adressliste wurde aus einer Tabellenkalkulation importiert. Postleitzahl und Ort stehen dort gemeinsam in der Spalte plz_ort, etwa '50667 Köln' oder '60311 Frankfurt am Main'. Zum Filtern nach Postleitzahlbereichen brauchst Du getrennte Spalten. Der Umbau gelingt in drei Schritten – Spalten ergänzen, Inhalte übertragen, alte Spalte löschen:
ALTER TABLE import
ADD COLUMN plz CHAR(5) NULL,
ADD COLUMN ort VARCHAR(60) NULL;
UPDATE import
SET plz = SUBSTRING_INDEX(TRIM(plz_ort), ' ', 1),
ort = TRIM(SUBSTRING(TRIM(plz_ort), LOCATE(' ', TRIM(plz_ort)) + 1));
-- erst prüfen: SELECT plz_ort, plz, ort FROM import;
ALTER TABLE import DROP COLUMN plz_ort;SUBSTRING_INDEX(text, trennzeichen, n) liefert den Teil vor dem n-ten Trennzeichen (bei negativem n den Teil nach dem n-ten Trennzeichen von rechts). Für den Ort wäre SUBSTRING_INDEX(plz_ort, ' ', -1) falsch: Aus „Frankfurt am Main“ bliebe nur „Main“. Deshalb sucht LOCATE das erste Leerzeichen, und SUBSTRING nimmt alles dahinter. Andere DBMS nennen die Zeichenkettenfunktionen anders (z. B. CHARINDEX in SQL Server, STRPOS in PostgreSQL, INSTR in Oracle und SQLite) – das Vorgehen bleibt gleich: erst die Wirkung der verfügbaren Funktion klären, dann den Ausdruck bauen. Datensätze ohne Leerzeichen oder mit unvollständiger Postleitzahl solltest Du vor dem DROP COLUMN gezielt suchen, etwa mit WHERE CHAR_LENGTH(plz) <> 5.
Berechnete Werte speichern: generierte Spalten
Manche Werte lassen sich aus anderen Spalten derselben Zeile ableiten. Im Vereinsbeispiel will der Vorstand oft wissen, wie viele Tage ein Gerät verliehen war – etwa um Dauerausleihen zu finden. Die Tabelle ausleihe enthält dafür schon von und bis. Naheliegend wäre eine zusätzliche Spalte, die einmal per UPDATE befüllt wird:
ALTER TABLE ausleihe ADD COLUMN dauer_tage SMALLINT UNSIGNED NULL;
UPDATE ausleihe SET dauer_tage = DATEDIFF(bis, von);Diese Spalte ist aber redundant: Wird ein Rückgabedatum nachgetragen oder korrigiert, stimmt dauer_tage nicht mehr, solange niemand daran denkt, sie neu zu berechnen. Besser ist eine generierte Spalte: MariaDB berechnet den Wert aus dem Ausdruck selbst und hält ihn bei jeder Änderung von von oder bis aktuell (anstelle der Spalte oben, nicht zusätzlich – sonst gäbe es den Spaltennamen doppelt):
ALTER TABLE ausleihe
ADD COLUMN dauer_tage SMALLINT AS (DATEDIFF(bis, von)) VIRTUAL;Solange ein Gerät noch nicht zurückgegeben ist (bis IS NULL), ist auch dauer_tage NULL. Mit VIRTUAL wird der Wert bei jedem Lesen berechnet und belegt keinen Speicher; STORED (in MariaDB auch PERSISTENT) legt ihn beim Schreiben ab und lohnt sich, wenn oft danach gefiltert oder sortiert wird. In InnoDB lassen sich beide Varianten indizieren. Selbst hineinschreiben kannst Du in eine generierte Spalte nicht. Erlaubt sind nur Ausdrücke, die bei gleichen Spaltenwerten immer dasselbe liefern – DATEDIFF(CURDATE(), von) für „seit wie vielen Tagen ausgeliehen“ lehnt MariaDB ab, weil sich CURDATE() täglich ändert. Solche Werte berechnest Du in der Abfrage oder in einer View.
Tabellenname anpassen
RENAME TABLE alterName TO neuerName;Gleichwertig ist ALTER TABLE alterName RENAME TO neuerName;.
Tabellen anzeigen
| MSSQL | MySQL / MariaDB | postgreSQL | OracleDB | SQLite | |
|---|---|---|---|---|---|
SELECT name FROM sys.tables; |
SHOW TABLES; |
SELECT tablename FROM pg_tables WHERE schemaname = 'public';in psql: \dt |
SELECT table_name FROM user_tables; |
SELECT name FROM sqlite_master WHERE type = 'table';in der sqlite3-Shell: .tables |
Tabellenstruktur anzeigen
Die Spalten einer Tabelle mit ihren Datentypen zeigen:
| MSSQL | MySQL / MariaDB | postgreSQL | OracleDB | SQLite | |
|---|---|---|---|---|---|
EXEC sp_help 'tabellenname'; |
DESCRIBE tabellenname;vollständige Anweisung: SHOW CREATE TABLE tabellenname; |
in psql: \d tabellenname |
DESCRIBE tabellenname (in SQL*Plus und SQL Developer) |
PRAGMA table_info(tabellenname);in der sqlite3-Shell: .schema tabellenname |
Tabellen löschen
DROP TABLE IF EXISTS tabellenname;DROP TABLE entfernt Struktur und Inhalt der Tabelle; mit IF EXISTS gibt es keinen Fehler, wenn die Tabelle nicht existiert. Davon zu unterscheiden:
| Anweisung | Teilsprache | Wirkung |
|---|---|---|
DELETE FROM tabelle [WHERE …]; |
DML | löscht (ausgewählte) Datensätze einzeln, Struktur bleibt, ON DELETE-Regeln greifen |
TRUNCATE TABLE tabelle; |
DDL | löscht alle Datensätze auf einmal (Tabelle wird intern neu angelegt), setzt AUTO_INCREMENT zurück, Struktur bleibt; nicht möglich, wenn ein Fremdschlüssel auf die Tabelle verweist |
DROP TABLE tabelle; |
DDL | löscht Tabelle samt Inhalt, Indizes und Constraints |
TRUNCATE und DROP lösen in MariaDB und MySQL ein implizites COMMIT aus und lassen sich dort nicht zurücknehmen.
Indizes zu Tabellen hinzufügen
Der Zugriff auf einzelne Datensätze erfolgt optimiert und schnell über die Primärschlüssel. Sollen bestimmte Datensätze über Nichtschlüsselattribute selektiert werden, so ist der Zugriff bei Tabellen mit vielen Datensätzen häufig deutlich langsamer, da es keine optimierten Zugriffsmethoden gibt. Ein Index hilft hier vor allem bei Spalten mit vielen unterschiedlichen Werten (siehe Abschnitt „Indizes haben ihren Preis“).
Ein Index ist eine zusätzliche, sortierte Zugriffsstruktur über die Werte einer oder mehrerer Spalten – vergleichbar mit dem Stichwortverzeichnis eines Buchs. In MariaDB (InnoDB) ist das in der Regel ein B-Baum. Statt die ganze Tabelle Zeile für Zeile zu lesen (full table scan), sucht das DBMS den Wert im Index und springt direkt zu den passenden Datensätzen. Davon profitieren Bedingungen in WHERE, Verknüpfungen in JOIN … ON und Sortierungen mit ORDER BY.
CREATE INDEX mein_indexname ON tabellenName (attribut);Als Beispiel könnte ein Index über das Attribut nachname der Adresstabelle folgendermaßen erzeugt werden:
CREATE INDEX idx_adressen_nachname ON tblAdressen (nachname);Der Zugriff auf die Datensätze erfolgt dann ähnlich optimiert wie für Primär- und Fremdschlüssel.
Indizes können auch über mehrere Attribute gebildet werden.
CREATE INDEX idx_adressen_plz_ort ON tblAdressen (plz, ort);Ein solcher zusammengesetzter Index ist zuerst nach plz, bei gleicher PLZ nach ort sortiert – wie ein Telefonbuch nach Nachname und Vorname. Er hilft bei Bedingungen auf plz allein und auf plz und ort gemeinsam, aber kaum bei einer Suche nur nach ort (leftmost prefix). Die am häufigsten gefilterte Spalte gehört deshalb nach vorn.
Weitere Varianten und Befehle:
CREATE UNIQUE INDEX uq_name ON tabelle (spalte);– Index, der zugleich doppelte Werte verbietet (wirkt wie einUNIQUE-Constraint). Gibt es schon Duplikate, scheitert die Anweisung.ALTER TABLE tabelle ADD INDEX name (spalte);– gleichwertige Schreibweise.DROP INDEX name ON tabelle;– Index löschen (in PostgreSQL, Oracle und SQLite:DROP INDEX name;),SHOW INDEX FROM tabelle;– vorhandene Indizes anzeigen.EXPLAIN SELECT …;zeigt, ob eine Abfrage einen Index nutzt (Spaltekey).
Manche Indizes entstehen automatisch: für den Primärschlüssel, für jede UNIQUE-Bedingung und – in InnoDB – für jede Fremdschlüsselspalte, sofern es noch keinen passenden Index gibt. Diese Spalten muss man also nicht zusätzlich indizieren.
Indizes haben ihren Preis
Jeder Index belegt zusätzlichen Speicherplatz.
Bei jedem
INSERT,UPDATEundDELETEmüssen alle betroffenen Indizes mitgepflegt werden – Schreibzugriffe werden langsamer.Bei kleinen Tabellen oder bei Spalten mit sehr wenigen unterschiedlichen Werten (z. B.
TRUE/FALSE) bringt ein Index kaum Vorteile; das DBMS liest dann trotzdem die ganze Tabelle.
Indiziert werden deshalb gezielt die Spalten, nach denen häufig gesucht, verknüpft oder sortiert wird – nicht „vorsichtshalber alle“.
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
- Welche Teilsprachen hat SQL, und zu welcher gehören
CREATE,ALTER,DROP,TRUNCATE,INSERTundGRANT?
SQL hat die Teilsprachen DDL (Struktur), DML (Inhalt ändern), DQL (Inhalt lesen, oft zur DML gezählt) und DCL (Berechtigungen). CREATE, ALTER, DROP und TRUNCATE gehören zur DDL, INSERT zur DML und GRANT zur DCL (siehe Abschnitt „Struktureinheiten in der DDL“).
- Wie wird aus einer Relation mit zusammengesetztem Primärschlüssel und zwei Fremdschlüsseln eine
CREATE TABLE-Anweisung?
Jedes Attribut wird zu einer Spalte mit Datentyp und Constraints. Der zusammengesetzte Primärschlüssel steht als eigene Zeile, z. B. PRIMARY KEY (geraet_id, mitglied_id, von), und jeder Fremdschlüssel bekommt eine eigene Zeile FOREIGN KEY (spalte) REFERENCES tabelle (spalte). Die Fremdschlüsselspalte muss denselben Datentyp (einschließlich UNSIGNED) haben wie der referenzierte Primärschlüssel (siehe Abschnitt „Vom Relationenmodell zur Tabelle“).
- Warum müssen Tabellen in einer bestimmten Reihenfolge angelegt und in umgekehrter Reihenfolge gelöscht werden?
Ein Fremdschlüssel kann nur auf eine Tabelle verweisen, die bereits existiert – deshalb legst Du zuerst die Tabellen ohne Fremdschlüssel an und dann die verweisenden. Beim Löschen ist es umgekehrt: Solange ein Fremdschlüssel auf eine Tabelle zeigt, verweigert MariaDB DROP TABLE (Fehler 1451), auch bei leeren Tabellen (siehe Abschnitt „Die Reihenfolge zählt“).
- Mit welchen Anweisungen fügst Du eine Spalte hinzu, änderst ihren Datentyp, benennst sie um und entfernst sie? Was ist bei
MODIFYzu beachten?
Mit ALTER TABLE … ADD [COLUMN] fügst Du eine Spalte hinzu, mit MODIFY änderst Du den Datentyp, mit CHANGE alt neu typ oder RENAME COLUMN alt TO neu benennst Du um, und mit DROP entfernst Du sie. Bei MODIFY musst Du die vollständige Spaltendefinition angeben – was fehlt, etwa NOT NULL oder DEFAULT, geht verloren (siehe Abschnitt „Tabellenstruktur anpassen“).
- Wie machst Du aus einer Spalte, in der schon Werte fehlen, ein Pflichtfeld?
Zuerst füllst Du die Lücken, z. B. mit UPDATE mitglied SET telefon = 'unbekannt' WHERE telefon IS NULL;, dann änderst Du die Spalte mit ALTER TABLE mitglied MODIFY COLUMN telefon VARCHAR(25) NOT NULL;. Ohne diesen ersten Schritt lehnt MariaDB die Änderung mit Fehler 1138 ab (siehe Abschnitt „Tabellenstruktur anpassen“).
- Worin unterscheiden sich
DELETE,TRUNCATEundDROP TABLE?
DELETE (DML) löscht ausgewählte Datensätze einzeln, die Struktur bleibt, und ON DELETE-Regeln greifen. TRUNCATE (DDL) löscht alle Datensätze auf einmal, setzt AUTO_INCREMENT zurück und ist nicht möglich, wenn ein Fremdschlüssel auf die Tabelle verweist. DROP TABLE (DDL) entfernt die Tabelle samt Inhalt, Indizes und Constraints; TRUNCATE und DROP lassen sich nicht zurücknehmen (siehe Abschnitt „Tabellen löschen“).
- Wozu werden Spalten indiziert, welche Nachteile haben Indizes, und welche Spalten sind ohnehin schon indiziert?
Ein Index ist eine sortierte Zugriffsstruktur, mit der das DBMS Werte direkt findet statt die ganze Tabelle zu lesen – das beschleunigt WHERE, JOIN … ON und ORDER BY. Nachteile: zusätzlicher Speicherplatz, langsamere Schreibzugriffe und kaum Nutzen bei kleinen Tabellen oder Spalten mit wenigen verschiedenen Werten. Automatisch indiziert sind Primärschlüssel, UNIQUE-Spalten und in InnoDB Fremdschlüsselspalten (siehe Abschnitt „Indizes zu Tabellen hinzufügen“ und Abschnitt „Indizes haben ihren Preis“).
- Warum hilft ein
ROLLBACKnicht, wenn Du versehentlich eine Spalte gelöscht hast?
DDL-Anweisungen wie ALTER TABLE … DROP lösen in MariaDB und MySQL ein implizites COMMIT aus: Eine laufende Transaktion wird festgeschrieben, und die DDL-Anweisung selbst lässt sich nicht mit ROLLBACK zurücknehmen. Deshalb brauchst Du vor dem Löschen von Spalten oder Tabellen eine Sicherung (siehe Abschnitt „Struktureinheiten in der DDL“).
Links und weitere Informationen
Wikibooks-Seite zu Abweichungen der SQL-Dialekte ggü. dem SQL-ANSI-Standard
als Nachschlagewerk: MariaDB Knowledge Base: CREATE TABLE und ALTER TABLE
als Nachschlagewerk: MariaDB Knowledge Base: Getting Started with Indexes
für die Praxis: MariaDB Knowledge Base: SQL statements that cause an implicit commit
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: “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-basics 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]
Teil 1 (Framework) kann bei der ISO erworben werden: https://www.iso.org/standard/76583.html↩︎
