Stored Procedures portabel schreiben: PL/pgSQL, T-SQL, PL/SQL
AI generated
SELECT
JOIN
SQL · Stored Procedures · PL/pgSQL · T-SQL · PL/SQL
Stored Procedures portabel schreiben
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.

19 Min. Lesezeit PL/pgSQL · T-SQL · PL/SQL · SQL/PSM PostgreSQL · SQL Server · Oracle

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.

11. FAQ: Stored Procedures portabel schreiben

1Kann man PL/pgSQL automatisch in T-SQL uebersetzen?
Teilweise, aber Cursor und Exception-Handling erfordern fast immer manuelle Ueberarbeitung.
2Was ist SQL/PSM?
Der ANSI-Standard fuer prozedurale Erweiterungen, wird von keinem grossen System strikt umgesetzt.
3Groesster Unterschied T-SQL vs. PL/pgSQL?
Explizite vs. implizite Cursor, und das @-Praefix bei Variablen in T-SQL.
4Was sind PL/SQL-Packages?
Namensraeume fuer Prozeduren und Funktionen, PostgreSQL hat kein direktes Aequivalent.
5Stored Procedures oder Anwendungslogik?
Meist Anwendung, Stored Procedures nur bei echten Performance-Vorteilen.
6Wie unterscheidet sich Fehlerbehandlung?
EXCEPTION-Block in PL/pgSQL und PL/SQL, TRY/CATCH in T-SQL, Konzepte aehnlich, nicht direkt uebertragbar.
7Was ist FORALL in PL/SQL?
Bulk-DML in einem Kontextwechsel, kein direktes Aequivalent in PL/pgSQL oder T-SQL.
8Wie teuer ist eine typische Migration?
Von Tagen bei einfachen Prozeduren bis Monaten bei umfangreichen Package-Strukturen.
9Gibt es Migrations-Tools?
Ja, wie ora2pg, liefern aber selten vollstaendig lauffaehigen Code ohne Nacharbeit.
10Lohnen sich Stored Procedures fuer neue Projekte?
Fuer performancekritische Operationen ja, fuer allgemeine Logik meist nicht.