PL/pgSQL, T-SQL und PL/SQL im Vergleich
Stored Procedures gehoeren zu den am wenigsten portablen Bausteinen von SQL, weil jedes grosse Datenbanksystem eine eigene, inkompatible prozedurale Sprache mitbringt. Dieser Artikel vergleicht PL/pgSQL, T-SQL und PL/SQL und zeigt konkrete Strategien, mit denen Geschaeftslogik in Stored Procedures trotzdem migrierbar bleibt.
Inhaltsverzeichnis
- 1. Warum Stored Procedures kaum portabel sind
- 2. Grundstruktur: CREATE PROCEDURE im Vergleich
- 3. Variablendeklaration und Zuweisung
- 4. Kontrollfluss: Bedingungen und Schleifen
- 5. Fehlerbehandlung: EXCEPTION, TRY/CATCH, EXCEPTION-Block
- 6. Cursor und Ergebnismengen verarbeiten
- 7. Rueckgabewerte und OUT-Parameter
- 8. Strategien fuer migrierbare Stored Procedures
- 9. PL/pgSQL, T-SQL und PL/SQL im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum Stored Procedures kaum portabel sind
Waehrend deklaratives SQL wie SELECT und JOIN zumindest in Teilen standardisiert ist, gilt das fuer Stored Procedures kaum. Der ANSI SQL Standard definiert mit SQL/PSM (Persistent Stored Modules) zwar eine prozedurale Erweiterung, doch keines der grossen kommerziellen Systeme hat sich strikt daran gehalten. PostgreSQL nutzt PL/pgSQL, Microsoft SQL Server nutzt Transact-SQL (T-SQL), und Oracle nutzt PL/SQL, drei Sprachen, die sich in Syntax, Fehlerbehandlung und Sprachumfang erheblich unterscheiden, obwohl sie aehnliche Konzepte verfolgen.
Diese fehlende Standardisierung bei Stored Procedures ist kein Zufall, sondern eine direkte Folge der Historie: Jedes System hat seine prozedurale Sprache zu einem Zeitpunkt entwickelt, als es noch keinen praktikablen Standard gab, und musste seither Abwaertskompatibilitaet zu Millionen Zeilen produktivem Code wahren. Wer Geschaeftslogik in Stored Procedures schreibt und spaeter die Datenbank wechseln muss, steht deshalb vor einer der aufwendigsten Migrationsaufgaben in der gesamten Datenbankwelt. Dieser Artikel zeigt, wo die drei Sprachen konkret abweichen und wie man Stored Procedures dennoch so strukturiert, dass eine spaetere Migration realistisch bleibt.
2. Grundstruktur: CREATE PROCEDURE im Vergleich
Schon die Grundstruktur einer Stored Procedure unterscheidet sich zwischen den drei Systemen deutlich. PostgreSQL definiert eine Funktion oder Prozedur mit CREATE FUNCTION beziehungsweise CREATE PROCEDURE, gefolgt von einem $$-quotierten Block, der den PL/pgSQL-Code enthaelt und mit LANGUAGE plpgsql abgeschlossen wird. SQL Server nutzt CREATE PROCEDURE ohne Dollar-Quoting, dafuer mit dem Schluesselwort AS vor dem Codeblock und BEGIN...END als Blockbegrenzer. Oracle strukturiert Stored Procedures in CREATE OR REPLACE PROCEDURE mit einem expliziten IS oder AS-Abschnitt vor der Deklaration lokaler Variablen und BEGIN...END fuer den Ausfuehrungsblock.
Diese strukturellen Unterschiede bei Stored Procedures wirken auf den ersten Blick kosmetisch, summieren sich aber bei einer Migration zu erheblichem manuellem Aufwand, weil praktisch jede einzelne Zeile Syntax-Anpassungen braucht. Ein automatisiertes Suchen und Ersetzen funktioniert dabei fast nie zuverlaessig, weil die drei Sprachen unterschiedliche Kommentarsyntax, unterschiedliche Terminierungszeichen und unterschiedliche Gross- und Kleinschreibungskonventionen fuer Schluesselwoerter pflegen.
-- PostgreSQL: PL/pgSQL, dollar-quoted body
CREATE OR REPLACE PROCEDURE update_stock(p_product_id INT, p_quantity INT)
LANGUAGE plpgsql
AS $$
BEGIN
UPDATE products
SET stock = stock - p_quantity
WHERE id = p_product_id;
END;
$$;
-- SQL Server: T-SQL
CREATE PROCEDURE UpdateStock
@ProductId INT,
@Quantity INT
AS
BEGIN
UPDATE Products
SET Stock = Stock - @Quantity
WHERE Id = @ProductId;
END;
-- Oracle: PL/SQL
CREATE OR REPLACE PROCEDURE update_stock(
p_product_id IN NUMBER,
p_quantity IN NUMBER
) IS
BEGIN
UPDATE products
SET stock = stock - p_quantity
WHERE id = p_product_id;
END update_stock;
/
3. Variablendeklaration und Zuweisung
Auch bei Variablen gehen die drei prozeduralen Sprachen eigene Wege. PL/pgSQL deklariert Variablen in einem separaten DECLARE-Abschnitt vor dem BEGIN-Block, mit dem Typ nach dem Variablennamen, aehnlich wie in Standard-SQL-Spaltendefinitionen. T-SQL nutzt das Praefix @ fuer alle Variablennamen und deklariert sie direkt im Code mit DECLARE @variable INT, ohne einen separaten Deklarationsblock zu erzwingen. PL/SQL folgt einer aehnlichen Struktur wie PL/pgSQL mit einem Deklarationsabschnitt vor BEGIN, verwendet aber den Zuweisungsoperator := konsequent, waehrend T-SQL sowohl SET @var = wert als auch SELECT @var = wert zulaesst.
Diese Unterschiede bei Stored Procedures wirken trivial, sind aber eine haeufige Fehlerquelle bei manueller Portierung, weil Entwickler aus Gewohnheit die Syntax der Ausgangssprache in die Zielsprache uebertragen. Ein PL/pgSQL-Entwickler, der versucht, T-SQL zu schreiben, vergisst leicht das @-Praefix, waehrend ein T-SQL-Entwickler in PL/pgSQL oft den Zuweisungsoperator := statt = vergisst, was in PostgreSQL zu einem subtilen, schwer zu findenden Syntaxfehler fuehrt.
-- PostgreSQL: PL/pgSQL variable declaration
CREATE OR REPLACE FUNCTION calculate_discount(p_total NUMERIC)
RETURNS NUMERIC
LANGUAGE plpgsql
AS $$
DECLARE
v_discount_rate NUMERIC := 0.1;
v_discount_amount NUMERIC;
BEGIN
v_discount_amount := p_total * v_discount_rate;
RETURN v_discount_amount;
END;
$$;
-- SQL Server: T-SQL, @ prefix, no separate declaration block
CREATE FUNCTION CalculateDiscount(@Total DECIMAL(10,2))
RETURNS DECIMAL(10,2)
AS
BEGIN
DECLARE @DiscountRate DECIMAL(4,2) = 0.1;
DECLARE @DiscountAmount DECIMAL(10,2);
SET @DiscountAmount = @Total * @DiscountRate;
RETURN @DiscountAmount;
END;
4. Kontrollfluss: Bedingungen und Schleifen
Bedingte Verzweigungen sehen in allen drei Sprachen auf den ersten Blick aehnlich aus, unterscheiden sich aber im Detail. PL/pgSQL nutzt IF ... THEN ... ELSIF ... ELSE ... END IF, PL/SQL folgt fast identischer Syntax mit ELSIF statt ELSE IF, waehrend T-SQL das schlichtere IF ... BEGIN ... END ELSE BEGIN ... END ohne dediziertes Schluesselwort fuer ELSIF verwendet, was bei vielen verschachtelten Bedingungen schnell unuebersichtlich wird.
Bei Schleifen zeigt sich die groesste Vielfalt: PL/pgSQL bietet LOOP, WHILE und FOR-Schleifen mit EXIT WHEN zum Abbrechen. PL/SQL kennt sehr aehnliche Konstrukte, ergaenzt um FORALL fuer performante Bulk-Operationen, ein Feature, das weder PL/pgSQL noch T-SQL in dieser Form kennen. T-SQL beschraenkt sich traditionell auf WHILE-Schleifen, weil FOR-Schleifen im klassischen Sinn fehlen, was mengenorientiertes Denken foerdert, aber bei zeilenweiser Verarbeitung zu umstaendlicherem Code fuehrt als in den anderen beiden Sprachen.
-- PostgreSQL: PL/pgSQL FOR loop over a query result
CREATE OR REPLACE PROCEDURE archive_old_orders()
LANGUAGE plpgsql
AS $$
DECLARE
r RECORD;
BEGIN
FOR r IN SELECT id FROM orders WHERE created_at < NOW() - INTERVAL '1 year'
LOOP
INSERT INTO orders_archive SELECT * FROM orders WHERE id = r.id;
DELETE FROM orders WHERE id = r.id;
END LOOP;
END;
$$;
-- SQL Server: T-SQL WHILE loop with a cursor-based approach
CREATE PROCEDURE ArchiveOldOrders
AS
BEGIN
DECLARE @OrderId INT;
DECLARE order_cursor CURSOR FOR
SELECT Id FROM Orders WHERE CreatedAt < DATEADD(year, -1, GETDATE());
OPEN order_cursor;
FETCH NEXT FROM order_cursor INTO @OrderId;
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO OrdersArchive SELECT * FROM Orders WHERE Id = @OrderId;
DELETE FROM Orders WHERE Id = @OrderId;
FETCH NEXT FROM order_cursor INTO @OrderId;
END;
CLOSE order_cursor;
DEALLOCATE order_cursor;
END;
5. Fehlerbehandlung: EXCEPTION, TRY/CATCH, EXCEPTION-Block
Fehlerbehandlung ist einer der Bereiche, in denen sich Stored Procedures zwischen den Systemen am staerksten in der Philosophie unterscheiden. PL/pgSQL nutzt einen EXCEPTION-Block innerhalb des BEGIN...END-Konstrukts, der spezifische Fehlerbedingungen wie WHEN unique_violation abfangen kann. T-SQL folgt seit SQL Server 2005 einem TRY...CATCH-Modell, das strukturell an Programmiersprachen wie C# erinnert und mit ERROR_MESSAGE(), ERROR_NUMBER() und aehnlichen Funktionen im CATCH-Block arbeitet.
PL/SQL nutzt ebenfalls einen EXCEPTION-Block, aber mit vordefinierten Namen wie NO_DATA_FOUND oder TOO_MANY_ROWS fuer haeufige Fehlerfaelle, ergaenzt um benutzerdefinierte Exceptions via RAISE_APPLICATION_ERROR. Diese drei unterschiedlichen Fehlerbehandlungsmodelle lassen sich nicht mechanisch ineinander uebersetzen, weil die verfuegbaren Fehlercodes, die Granularitaet der Fehlerbehandlung und selbst das Konzept von verschachtelten Transaktionen bei Fehlern zwischen den Systemen abweichen.
-- PostgreSQL: EXCEPTION block within PL/pgSQL
CREATE OR REPLACE PROCEDURE safe_insert_customer(p_email TEXT)
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO customers (email) VALUES (p_email);
EXCEPTION
WHEN unique_violation THEN
RAISE NOTICE 'Customer with email % already exists', p_email;
END;
$$;
-- SQL Server: TRY/CATCH block in T-SQL
CREATE PROCEDURE SafeInsertCustomer @Email VARCHAR(255)
AS
BEGIN
BEGIN TRY
INSERT INTO Customers (Email) VALUES (@Email);
END TRY
BEGIN CATCH
PRINT 'Error: ' + ERROR_MESSAGE();
END CATCH
END;
6. Cursor und Ergebnismengen verarbeiten
Alle drei Sprachen unterstuetzen Cursor fuer die zeilenweise Verarbeitung von Ergebnismengen, doch der explizite Umgang damit unterscheidet sich. PL/pgSQL kapselt Cursor-Logik meist implizit ueber FOR record IN query LOOP, was den expliziten OPEN/FETCH/CLOSE-Zyklus fuer die meisten Anwendungsfaelle ueberfluessig macht. T-SQL erfordert traditionell den expliziten Zyklus mit DECLARE CURSOR, OPEN, FETCH NEXT in einer WHILE-Schleife und abschliessendem CLOSE und DEALLOCATE, was deutlich mehr Boilerplate erzeugt.
PL/SQL bietet beide Varianten: implizite Cursor ueber FOR record IN (SELECT ...) LOOP, aehnlich wie PL/pgSQL, und explizite Cursor mit CURSOR cursor_name IS SELECT ... fuer Faelle, in denen mehr Kontrolle noetig ist, etwa bei parametrisierten Cursorn oder bei Bulk-Fetch-Operationen mit BULK COLLECT. Fuer Migrationen bedeutet das: T-SQL-Cursor-Code ist meist der aufwendigste Teil einer Portierung, weil er in PL/pgSQL fast immer zu einer deutlich kompakteren impliziten Schleife vereinfacht werden kann, aber diese Vereinfachung erfordert manuelle Analyse statt automatischer Uebersetzung.
7. Rueckgabewerte und OUT-Parameter
Bei Rueckgabewerten unterscheiden sich Stored Procedures ebenfalls grundlegend. PostgreSQL trennt konzeptionell zwischen Funktionen, die immer einen Wert via RETURN liefern, und Prozeduren, die stattdessen OUT oder INOUT-Parameter fuer Rueckgabewerte nutzen, seit PostgreSQL 11 auch mit echtem CALL-Aufruf statt SELECT. T-SQL kennt OUTPUT-Parameter direkt in der Prozedursignatur und zusaetzlich einen impliziten Integer-Rueckgabewert ueber RETURN, der traditionell fuer Statuscodes genutzt wird, nicht fuer fachliche Daten.
PL/SQL unterscheidet zwischen Funktionen (immer mit RETURN-Wert) und Prozeduren (mit IN, OUT und IN OUT-Parametern), aehnlich wie PostgreSQL, erlaubt aber zusaetzlich Package-basierte Organisation von mehreren zusammengehoerigen Prozeduren und Funktionen in einem benannten Namensraum, ein Konzept, das weder PostgreSQL noch SQL Server in dieser Form kennen. Diese Package-Struktur ist einer der Gruende, warum grosse PL/SQL-Codebasen bei einer Migration besonders aufwendig zu zerlegen sind, weil die Package-Grenzen selten sauber mit modularen Grenzen in anderen Systemen uebereinstimmen.
8. Strategien fuer migrierbare Stored Procedures
Angesichts der erheblichen Unterschiede zwischen PL/pgSQL, T-SQL und PL/SQL lohnt sich eine bewusste Architekturentscheidung, bevor Geschaeftslogik in Stored Procedures geschrieben wird. Der wichtigste Grundsatz: Stored Procedures sollten sich auf Operationen beschraenken, die tatsaechlich von der Naehe zu den Daten profitieren, etwa Bulk-Updates, komplexe mengenbasierte Aggregationen oder Operationen, die viele Round-Trips zur Anwendung sparen. Reine Geschaeftsregeln, die auch in der Anwendungsschicht funktionieren wuerden, gehoeren dort besser aufgehoben, weil Anwendungscode in der Regel deutlich portabler zwischen Datenbanksystemen ist als prozedurales SQL.
Wo Stored Procedures unvermeidbar sind, hilft eine konsequente Trennung zwischen einfachen, mechanisch uebersetzbaren Konstrukten (Variablendeklaration, einfache Bedingungen, Standard-SELECT/INSERT/UPDATE) und komplexen, sprachspezifischen Konstrukten (Cursor, Exception-Handling, Package-Strukturen). Wer die komplexen Teile bewusst klein und gut dokumentiert haelt, reduziert den Migrationsaufwand erheblich, selbst wenn eine vollstaendige Portabilitaet zwischen PL/pgSQL, T-SQL und PL/SQL realistischerweise nie erreichbar sein wird.
9. PL/pgSQL, T-SQL und PL/SQL im direkten Vergleich
Die folgende Tabelle fasst die wichtigsten strukturellen Unterschiede zwischen den drei prozeduralen Sprachen zusammen.
| Aspekt | PL/pgSQL | T-SQL | PL/SQL |
|---|---|---|---|
| Variablenpraefix | Kein Praefix | @variable |
Kein Praefix |
| Fehlerbehandlung | EXCEPTION WHEN |
TRY...CATCH |
EXCEPTION WHEN |
| Implizite Cursor | Ja, ueber FOR-Loop | Nein, immer explizit | Ja, ueber FOR-Loop |
| Namensraum-Organisation | Schemas | Schemas | Packages (einzigartig) |
Diese Gegenueberstellung zeigt, warum Stored Procedures selbst bei aehnlicher fachlicher Aufgabe fast immer neu geschrieben statt uebersetzt werden muessen. Der Migrationsaufwand haengt stark davon ab, wie viele sprachspezifische Features wie Package-Strukturen, FORALL-Bulk-Operationen oder explizite Cursor tatsaechlich genutzt wurden.
Mironsoft
Stored-Procedure-Migration und prozedurale SQL-Beratung
PL/SQL, T-SQL oder PL/pgSQL migrieren?
Wir analysieren bestehende Stored Procedures, identifizieren sprachspezifische Risikobereiche wie Cursor und Package-Strukturen und entwickeln eine realistische Migrationsstrategie zwischen PL/pgSQL, T-SQL und PL/SQL.
Code-Analyse
Inventarisierung aller Stored Procedures und ihrer sprachspezifischen Konstrukte
Portierung
Manuelle Uebersetzung komplexer Logik zwischen PL/pgSQL, T-SQL und PL/SQL
Refactoring-Beratung
Verlagerung geeigneter Geschaeftslogik in die portablere Anwendungsschicht
10. Zusammenfassung
Stored Procedures sind eine der am wenigsten portablen Ebenen von SQL, weil PL/pgSQL, T-SQL und PL/SQL trotz aehnlicher Grundkonzepte in Syntax, Fehlerbehandlung, Cursor-Verarbeitung und Namensraum-Organisation erheblich voneinander abweichen. Der SQL/PSM-Standard existiert zwar auf dem Papier, wird aber von keinem der drei Systeme strikt umgesetzt, sodass jede Migration zwischen diesen Sprachen im Kern eine manuelle Neuentwicklung bleibt, keine mechanische Uebersetzung.
Wer Stored Procedures bewusst auf Operationen beschraenkt, die wirklich von Datenbanknaehe profitieren, und komplexe, sprachspezifische Konstrukte wie Package-Strukturen oder explizite Cursor moeglichst vermeidet, reduziert den Migrationsaufwand erheblich. Die pragmatischste Strategie bleibt, Geschaeftslogik primaer in der Anwendungsschicht zu halten und Stored Procedures gezielt fuer performancekritische, datennahe Operationen einzusetzen, mit klarer Dokumentation, welche Sprachfeatures jeweils genutzt wurden.
Stored Procedures portabel schreiben: Das Wichtigste auf einen Blick
Keine echte Standardisierung
SQL/PSM existiert, wird aber von PostgreSQL, SQL Server und Oracle nicht strikt umgesetzt.
Groesste Unterschiede
Fehlerbehandlung, Cursor-Handling und Package-Strukturen sind am wenigsten kompatibel.
Migrations-Strategie
Einfache Konstrukte mechanisch uebersetzen, komplexe Teile manuell neu entwickeln.
Pragmatischer Rat
Geschaeftslogik primaer in der Anwendungsschicht, Stored Procedures nur fuer datennahe Operationen.