Schema-Migrationen
Kurz gesagt: Eine Schema-Migration ist eine nummerierte Änderung am Schema der Datenbank, die einmal läuft und die Datenbank auf jedem Rechner auf derselben Version hält.
Problem
„no such column: phone“
Ein Kollege legt auf seinem Computer eine Spalte phone in customers an und schreibt den Code, der sie anzeigt. Auf seinem Computer funktioniert alles. Die neue Version geht auf die Seite, und die Seite „Kunden“ scheitert mit no such column: phone, weil die Datenbank der Seite diese Spalte nicht hat. Und ein dritter Computer im Team hat zwei Spalten, weil dort jemand sie von Hand unter einem anderen Namen angelegt hat.
Der Code ist in git, und alle haben dieselbe Version. Das Schema der Datenbank braucht dasselbe: aufgeschrieben, versioniert und überall gleich.
Die Idee
Eine Migration: eine nummerierte Änderung
Eine Migration ist eine kleine SQL-Datei mit einer Nummer. Jede Änderung am Schema ist eine neue Datei, und die Dateien liegen in git neben dem Code:
migrations/ 001_create_shop.sql 002_reviews.sql 003_customers_phone.sqlDie Datenbank merkt sich in einer Tabelle, welche gelaufen sind, meist schema_migrations. Beim Start (oder wenn eine neue Version ausgeliefert wird) führt das Werkzeug der Reihe nach nur die Dateien aus, die noch nicht darin stehen. Diese Datenbank ist auf Version 2:
Migration 3 ist die Änderung plus ihr Eintrag, zusammen in einer Transaktion:
Führ sie ein zweites Mal im selben Feld aus, und ALTER TABLE bleibt mit „duplicate column name“ stehen. Deshalb prüft das Werkzeug schema_migrations, bevor es eine Datei ausführt.
Sieh es dir an
Warum in einer Transaktion
Wenn eine Migration ohne Transaktion auf halbem Weg stehen bleibt, bleibt die erste Änderung, und der Eintrag der Version fehlt. Der nächste Versuch beginnt wieder von vorn und stolpert über seinen eigenen ersten Schritt. Hier ist die zweite Anweisung ein Fehler (dieselbe Spalte zweimal):
Die Transaktion bleibt mit dem ersten ALTER TABLE offen, und das COMMIT erreicht die Datenbank nie. Führ im Feld ROLLBACK; aus, und die Spalte phone verschwindet: In SQLite und PostgreSQL gehört eine Schemaänderung zur Transaktion. Du korrigierst die Datei und führst sie noch einmal aus, und die Datenbank ist genau so, wie sie war.
Probier es aus
Migration 3: Treuepunkte
Schreib Migration 3 für die Datenbank auf Version 2: eine Spalte loyalty_points in customers (eine ganze Zahl, Pflichtfeld, standardmäßig 0) und den Eintrag (3, 'loyalty_points', '2026-10-25') in schema_migrations, in einer Transaktion.
Lösung
BEGIN;
ALTER TABLE customers ADD COLUMN loyalty_points INTEGER NOT NULL DEFAULT 0;
INSERT INTO schema_migrations VALUES (3, 'loyalty_points', '2026-10-25');
COMMIT;Ohne DEFAULT 0 fügt SQLite einer Tabelle mit Zeilen keine NOT NULL-Spalte hinzu, weil die 20 Kunden keinen Wert dafür haben. Mit DEFAULT 0 bekommt jeder 0. Der Eintrag in schema_migrations steht in derselben Transaktion, damit die Migration nicht zweimal läuft.
Die Idee
Die Werkzeuge
Niemand schreibt die Prüfung „welche Migration ist gelaufen“ von Hand. Jede Sprache hat ein Werkzeug, das die Versionstabelle führt und die Dateien der Reihe nach ausführt:
- Java: Flyway oder Liquibase (
V3__customers_phone.sql); - C#: EF Core Migrations (
dotnet ef migrations add CustomersPhone) schreibt die Migration aus der Änderung in den Klassen; - Python: Alembic (für SQLAlchemy) oder Django-Migrationen;
- JavaScript: Prisma Migrate, Knex oder node-pg-migrate.
Manche schreiben auch einen Rückwärtsschritt (down), der die Migration rückgängig macht. Auf einer Seite mit echten Daten ist der Weg zurück selten ein DROP COLUMN: Wenn die neue Spalte schon Daten enthält, verliert der Rückwärtsschritt sie. „Zurück“ ist deshalb meist eine neue Migration nach vorn.
Sieh es dir an
Eine laufende Datenbank ändern: in Schritten
Die Seite steht nicht still, während die neue Version ausgeliefert wird: Eine Weile laufen der alte und der neue Code nebeneinander. Wenn die Migration city in town umbenennt, scheitert der alte Code in derselben Sekunde. Deshalb geschieht die Änderung in drei Schritten:
- Erweitern.
townhinzufügen und auscityfüllen. Der alte Code merkt nichts davon. - Umstellen. Der neue Code schreibt in beide und liest
town. Du wartest, bis der alte Code überall weg ist. - Zurückbauen. Eine eigene, spätere Migration entfernt
city.
Übung
Vor- und Nachname getrennt
Der Shop will Kunden mit dem Vornamen begrüßen („Hallo, Maria“). Mach den ersten Schritt (Erweitern): Füge die Spalten first_name und last_name zu customers hinzu und fülle sie aus name (jeder Name besteht aus zwei Wörtern, getrennt durch ein Leerzeichen). Die Spalte name bleibt.
Lösung
ALTER TABLE customers ADD COLUMN first_name TEXT;
ALTER TABLE customers ADD COLUMN last_name TEXT;
UPDATE customers
SET first_name = substr(name, 1, instr(name, ' ') - 1),
last_name = substr(name, instr(name, ' ') + 1);instr(name, ' ') gibt die Position des Leerzeichens, und substr nimmt den Text davor und danach. Die Spalte name bleibt, weil der alte Code sie noch liest. Sie zu entfernen ist der letzte Schritt, in einer eigenen Migration.
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: ALTER TABLE: eine Tabelle ändern · ACID: alles oder nichts · BEGIN, COMMIT, ROLLBACK · Aus dem Code mit der Datenbank verbinden
- Geht weiter in: Verbindungspools, Backups und Berechtigungen
- Aufgaben zum Thema: Die Datenbank im Programm