Datenmigration: SQL Server nach PostgreSQL — der vollständige Leitfaden

Eine Datenmigration von SQL Server nach PostgreSQL scheitert selten am eigentlichen Kopieren der Daten. Sie scheitert an den stillen Unterschieden, die erst im Ziel auffallen: datetime, das keine Zeitzone kennt, bit, das kein boolean ist, ein IDENTITY, das zur Sequenz wird, und eine Collation, die plötzlich case-sensitiv vergleicht. Wer SQL Server nach PostgreSQL migrieren will, kopiert nicht einfach Tabellen — er übersetzt Typen, Schema, Code und Verhalten von einer Engine in eine andere.

Dieser Leitfaden ordnet den Umzug in fünf Phasen und benennt für jede die wichtigsten Stolperfallen. In die Tiefe geht jeweils ein eigener Artikel — dieser hier liefert den roten Faden, der die Phasen verbindet.

Das Wichtigste vorab:

  • Fünf Phasen statt „Daten-Umzug“: Der Umzug zerfällt in Datentypen, Schema/DDL, Datentransfer, Code-Portierung und Verifikation. Wer sie in dieser Reihenfolge angeht, erspart sich die typischen Rückschläge.
  • Jede Phase mit eigener Stolperfalle: Mal sind es kippende Typen, mal wird IDENTITY zur Sequenz, mal die Transfer-Methode oder die Übersetzung von T-SQL nach PL/pgSQL. Keine davon fällt auf, bis sie im Ziel zuschlägt — deshalb steht am Ende die Verifikation.
  • Was Tooling abnimmt: Werkzeuge wie pgloader erledigen die mechanischen rund 80 % — die unkritischen Typen und den Massentransfer. Die letzten 20 % — Typ-Edge-Cases, Prozedur-Logik, Trigger — bleiben Handarbeit.
  • Der rote Faden: Jede Phase hat ihren eigenen Detail-Artikel mit der vollen Tiefe. Dieser Hub verbindet sie und sagt, in welcher Reihenfolge sie zusammenspielen.

Voraussetzung: Grundverständnis relationaler Datenbanken. SQL Server 2017+ als Quelle, PostgreSQL 16/17 als Ziel. Postgres-Konzepte (Sequenzen, textCOPY, PL/pgSQL) werden bei der ersten Nennung kurz eingeordnet — Vorwissen über Postgres ist nicht nötig, T-SQL-Grundlagen schon.

Inhalt

Warum SQL Server nach PostgreSQL migrieren?

Die Gründe sind selten technischer Natur — sie sind meist wirtschaftlich oder strategisch. Lizenzkosten fallen weg: PostgreSQL ist quelloffen und ohne Core- oder CAL-Lizenzen nutzbar. Der Open-Source-Stack lässt sich frei auf jeder Infrastruktur betreiben, ohne Bindung an einen Hersteller. Und die Cloud-Portabilität ist hoch — nahezu jeder Anbieter betreibt verwaltetes PostgreSQL.

Das ist kein Glaubenskrieg „Postgres besser als SQL Server“. Beide Engines sind ausgereift, und nicht jede Last gehört umgezogen. Der Punkt ist nüchtern: Wenn die Entscheidung gefallen ist, SQL Server nach PostgreSQL zu migrieren, lohnt es sich, den Umzug als strukturierten Pfad zu planen statt als einmalige Kopier-Aktion. Genau dafür ist dieser Leitfaden da.

Der Migrationspfad in fünf Phasen

Eine Datenbank-Migration ist kein einzelner Schritt, sondern eine Kette abhängiger Phasen. Wer sie in dieser Reihenfolge angeht, vermeidet die typischen Rückschläge — etwa Daten zu transferieren, bevor das Zielschema die richtigen Typen hat. Jede Phase hat ihren eigenen Detail-Artikel.

1. Datentypen. Die Grundlage. Die meisten Typen konvertieren eins zu eins (intnumericvarchardate) — aber datetimebitmoneyuniqueidentifiernvarchar und tinyint haben keine 1:1-Entsprechung. datetime zwingt zur Zeitzonen-Frage (timestamp vs. timestamptz), bit wird zum echten boolean, vom money-Typ in Postgres lässt man besser die Finger. Welche Typen sauber konvertieren und welche kippen, klärt der Artikel Datentyp-Mapping SQL Server → PostgreSQL.

2. Schema & DDL. Hat man die Typen, kommt die Struktur drumherum: IDENTITY wird zu GENERATED AS IDENTITY oder einer Sequenz, Default-Constraints und benannte Constraints wandern um, und die Groß-/Kleinschreibung von Bezeichnern kippt von SQL-Server-tolerant zu Postgres-exakt. Details in Schema-Migration SQL Server → PostgreSQL.

3. Datentransfer. Erst wenn das Zielschema steht, wandern die Daten. Die Methodenwahl hängt von Datenmenge und Downtime-Toleranz ab: bcp-Export plus COPY, das All-in-one-Werkzeug pgloader oder eine ETL-Strecke. Welche Methode wann passt, vergleicht Daten transferieren: bcp, COPY, pgloader, ETL.

4. Code-Portierung. Tabellen sind nur die halbe Datenbank. Stored Procedures, Funktionen und Trigger müssen von T-SQL nach PL/pgSQL übersetzt werden — andere Fehlerbehandlung, andere Variablen-Syntax, andere Idiome (TRY_CONVERT hat kein direktes Gegenstück). Das ist der Teil mit dem höchsten Handarbeits-Anteil. Vertieft in T-SQL nach PL/pgSQL portieren.

5. Verifikation. Eine Migration ist erst fertig, wenn bewiesen ist, dass nichts verloren ging oder still verfälscht wurde: Zeilen-Abgleich pro Tabelle, Stichproben-Vergleiche, Datenqualitäts-Checks nach dem Load. Wie man das systematisch prüft, zeigt Migration verifizieren — Datenqualität und Zeilen-Abgleich.

Die Phasen bauen aufeinander auf, lassen sich aber iterieren — typisch ist, Typen und Schema gemeinsam an einer Pilot-Tabelle durchzuspielen, bevor die volle Last transferiert wird.

Eine Tabelle, mehrere Phasen auf einmal

Damit der Phasen-Pfad greifbar wird, eine kleine Tabelle, die mehrere Stolperfallen auf einmal bündelt. Zuerst die Quelle in T-SQL:

  1: CREATE TABLE dbo.customer
  2: (
  3:     customer_id   int             IDENTITY(1, 1)  NOT NULL
  4:    ,full_name     nvarchar(100)   NOT NULL
  5:    ,is_active     bit             NOT NULL  DEFAULT 1
  6:    ,created_at    datetime        NOT NULL  DEFAULT GETDATE()
  7:    ,CONSTRAINT pk_customer PRIMARY KEY (customer_id)
  8: );

Und dasselbe als PostgreSQL-Ziel:

  1: CREATE TABLE customer
  2: (
  3:     customer_id   integer       GENERATED BY DEFAULT AS IDENTITY
  4:    ,full_name     text          NOT NULL
  5:    ,is_active     boolean       NOT NULL  DEFAULT true
  6:    ,created_at    timestamp     NOT NULL  DEFAULT now()
  7:    ,CONSTRAINT pk_customer PRIMARY KEY (customer_id)
  8: );

Vier Spalten, und schon greifen zwei Phasen ineinander — Datentypen und Schema — an einer Tabelle, die niemand für migrationskritisch halten würde. Drei der Änderungen sieht man kommen:

  • Zeile 3 (Schema): int IDENTITY(1, 1) → integer GENERATED BY DEFAULT AS IDENTITY — die Auto-Wert-Spalte wandert auf den SQL-Standard-Mechanismus. Die eigentliche Arbeit kommt nach dem Laden: Die Sequenz muss auf den Höchstwert nachgezogen werden, sonst kollidiert der nächste INSERT.
  • Zeile 5 (Datentyp): bit → boolean, aus 1 wird true. Vorsicht: portierte Abfragen wie WHERE is_active = 1 brechen in Postgres.
  • Zeile 6 (Datentyp): datetime → timestamp. Die Falle ist nicht now() statt GETDATE(), sondern dass datetime keine Zeitzone trägt: Ob timestamp oder timestamptz richtig ist, hängt davon ab, ob die Werte als lokale Zeit oder als UTC gemeint waren — trifft man die Entscheidung nicht bewusst, verschiebt sie sich später still.

Die vierte fällt beim Überfliegen durch — und rutscht am ehesten unbemerkt durch:

  • Zeile 4 (Datentyp): nvarchar(100) → text. „Postgres ist nativ Unicode, also text“ stimmt — verschweigt aber, dass die Längenbegrenzung verschwindet. Genau so greift ein Auto-Konverter wie pgloader standardmäßig zu: nvarchar(100) wird zu text, die Grenze fällt ungefragt weg. War die 100 nur technischer Ballast, ist text die richtige Wahl. War sie eine fachliche Regel — die Datenbank durfte nichts Längeres annehmen —, ist eine stille Validierung verloren gegangen, und dahin gehört ein varchar(100) oder eine CHECK-Bedingung statt nacktem text. Das Tool trifft die Übersetzung automatisch — aber ob die Grenze fachlich gewollt war, kann es nicht wissen: Diese Frage steht nicht im Typ, sondern in der Fachlichkeit.

Das ist der ganze Leitfaden in einer Tabelle: Vier Zeilen, die nach Suchen-und-Ersetzen aussehen, tragen zwei Phasen und mindestens eine Falle, die man nur sieht, wenn man die Daten fachlich kennt — nicht bloß ihren Typ.

Was Tooling abnimmt — und wo Handarbeit bleibt

Die ehrliche Erwartungssteuerung vorweg: Es gibt kein „ein Klick und fertig“. Werkzeuge wie pgloader (frei, übernimmt Schema und Daten in einem Lauf) oder kommerzielle Konverter nehmen den mechanischen Großteil ab — und das ist viel: die unkritischen Typen, der Massentransfer, die Standard-Constraints. Als Faustregel decken sie rund 80 % der Mechanik.

Die verbleibenden 20 % sind genau die Stellen, an denen eine fachliche Entscheidung nötig ist, die kein Tool kennen kann:

  • Kippende Typen — datetime → timestamp oder timestamptzmoney → welche numeric-Skala? tinyint mit erhaltender CHECK-Bedingung? Diese Fälle gehören geprüft, nicht blind übernommen.
  • Prozedur- und Trigger-Logik — T-SQL nach PL/pgSQL ist Übersetzungsarbeit, kein Suchen-und-Ersetzen.
  • Performance-Tuning — Indizes, Statistiken und Abfragepläne unterscheiden sich; was in SQL Server schnell war, braucht in Postgres ggf. einen anderen Index.
  • Verhaltens-Unterschiede — Collation/Case, NULL-Sortierung, Transaktions-Semantik bei Fehlern.

Die Kunst der Migration liegt nicht im Transfer der einfachen 80 %, sondern in der sauberen, geprüften Übersetzung der schwierigen 20 %. Dieser Cluster widmet jeder dieser Stellen einen eigenen Artikel.

FAQ

Wie lange dauert eine SQL-Server-→-Postgres-Migration?

Das hängt von Schema-Komplexität und Code-Menge ab, nicht primär von der Datenmenge. Der Datentransfer selbst ist oft in Stunden erledigt; die Zeit geht in die Code-Portierung (Stored Procedures, Trigger) und die Verifikation. Eine einfache Datenbank mit wenig Logik ist in Tagen machbar, eine mit hunderten Prozeduren in Wochen bis Monaten.

Kann ich das komplett automatisieren?

Nein. Werkzeuge wie pgloader nehmen die mechanischen ~80 % ab — die unkritischen Typen und den Massentransfer. Die letzten 20 % (kippende Typen, Prozedur-Logik, Trigger, Performance-Tuning) brauchen menschliche Entscheidungen. Wer „ein Klick und fertig“ erwartet, baut stille Fehler ein.

Was passiert mit meinen Stored Procedures?

Sie müssen von T-SQL nach PL/pgSQL portiert werden — das ist die aufwändigste Phase. Fehlerbehandlung (TRY/CATCH → EXCEPTION), Variablen-Syntax und viele Idiome unterscheiden sich. Ein direktes Übersetzungs-Tool gibt es nicht; der eigene Artikel zur Code-Portierung zeigt die Muster.

Brauche ich Downtime?

Für die einfachste Variante (Export → Transfer → Umschalten) ja — die Quelle ist während des Transfers idealerweise read-only, damit keine Änderungen verloren gehen. Downtime-arme Strategien (logische Replikation, schrittweiser Cutover) sind möglich, aber deutlich aufwändiger und ein Thema für sich.

Migriere ich besser schrittweise oder als Big Bang?

Für die meisten Solo- und Mittelstands-Szenarien ist der Big-Bang-Cutover in einem ruhigen Wartungsfenster pragmatisch. Schrittweise Migration (beide Systeme parallel) lohnt sich bei großen, durchgehend verfügbaren Systemen — erkauft aber mit erheblicher Synchronisations-Komplexität.

Funktioniert das auch für Azure SQL oder andere Quell-Datenbanken?

Die Phasen (Typen → Schema → Transfer → Code → Verifikation) gelten allgemein, und Azure SQL ist eng mit SQL Server verwandt — vieles überträgt sich. Dieser Cluster ist aber konkret auf SQL Server → PostgreSQL zugeschnitten; Oracle oder MySQL als Quelle bringen andere Typ- und Dialekt-Fallen mit.

Verwandte Artikel

Dieser Leitfaden ist der Überblick des Clusters zur Migration von SQL Server nach PostgreSQL. Jede Phase hat ihren eigenen Detail-Artikel:

Zur Vertiefung angrenzender Themen: