CodeGym /Kurse /SQL SELF /Aufruf von Prozeduren und Funktionen innerhalb von Transa...

Aufruf von Prozeduren und Funktionen innerhalb von Transaktionen

SQL SELF
Level 53 , Lektion 1
Verfügbar

In modernen Datenbanksystemen wird die Business-Logik oft serverseitig umgesetzt – mit Prozeduren und Funktionen. Wenn du mit PostgreSQL arbeitest, ist es wichtig, den Unterschied zwischen Funktionen und Prozeduren zu verstehen (vor allem seit es Prozeduren ab Version 11+ gibt) und wie sie mit Transaktionen zusammenspielen.

Hier erzähle ich dir die wichtigsten Fakten zur Transaktionsmechanik, verschachtelten Aufrufen und teilweisem Rollback von Änderungen in Prozeduren/Funktionen von PostgreSQL 17 – laut offizieller Doku und aktuellen Einschränkungen.

Wichtige Begriffe: Funktionen vs Prozeduren

Funktion (CREATE FUNCTION) – läuft immer im Rahmen einer äußeren Transaktion; innerhalb von Funktionen darfst du keine expliziten Transaktionsbefehle (BEGIN, COMMIT, ROLLBACK, SAVEPOINT) verwenden.

  • Alle Änderungen werden nur auf Ebene der äußeren Transaktion festgeschrieben oder zurückgesetzt.
  • Für einen „teilweisen Rollback“ innerhalb von Funktionen nutzt man BEGIN ... EXCEPTION ... END, aber damit kann man keine Commits innerhalb der Funktion machen.

Prozedur (CREATE PROCEDURE) – wurde eingeführt, um Transaktionen direkt auf dem Server zu steuern (z.B. Teil-Commits, Rollbacks von Schritten usw.).

  • In Prozeduren (PL/pgSQL) kannst du COMMIT, ROLLBACK, SAVEPOINT, RELEASE SAVEPOINT verwenden.
  • WICHTIG: ROLLBACK TO SAVEPOINT ist in einer PL/pgSQL-Prozedur nicht erlaubt (gibt einen Syntaxfehler).
  • Prozeduren kann man nur mit dem separaten SQL-Befehl CALL ... aufrufen, nicht per SELECT oder innerhalb anderer Funktionen.

Wie ruft man eine Prozedur/Funktion aus einer anderen auf?

Funktionen rufen andere Funktionen „transparent“ über den Funktionsnamen auf:

-- Beispiel: Funktion zur Berechnung des Rabatts
CREATE OR REPLACE FUNCTION berechne_rabatt(bestellwert NUMERIC)
RETURNS NUMERIC AS $$
BEGIN
    IF bestellwert >= 100 THEN
        RETURN bestellwert * 0.1;
    ELSE
        RETURN 0;
    END IF;
END;
$$ LANGUAGE plpgsql;

-- Bestellverarbeitungsfunktion ruft eine andere Funktion auf
CREATE OR REPLACE FUNCTION bestellung_verarbeiten(bestell_id INT, bestellwert NUMERIC)
RETURNS VOID AS $$
DECLARE
    rabatt NUMERIC;
BEGIN
    rabatt := berechne_rabatt(bestellwert);
    RAISE NOTICE 'Rabatt: %', rabatt;
    INSERT INTO bestellungen_log (bestell_id, bestellwert, rabatt)
    VALUES (bestell_id, bestellwert, rabatt);
END;
$$ LANGUAGE plpgsql;

Alles läuft in einer äußeren Transaktion! Ein Fehler in einer Funktion führt zum Rollback aller Änderungen.

Aufruf von Prozeduren und verschachtelte Transaktionen

Prozeduren kann man innerhalb anderer Prozeduren mit CALL ... aufrufen (in PostgreSQL 17 ist ein Aufruf-Stack wie CALL proc1() -> CALL proc2() erlaubt), aber die Transaktionsregeln bleiben:

  • Transaktionsbefehle (COMMIT, ROLLBACK, SAVEPOINT, RELEASE SAVEPOINT) sind nur auf der obersten Ebene von Prozeduren erlaubt.
  • Wenn eine Prozedur mit Transaktionssteuerung innerhalb einer bereits aktiven expliziten Transaktion aufgerufen wird (z.B. über einen Client ohne Autocommit), führt ein Versuch, COMMIT/SAVEPOINT auszuführen, zu einem Fehler.
WICHTIG:

Prozeduren dürfen nicht innerhalb von Funktionen oder anonymen Blöcken (DO ...) gestartet werden. Nur mit dem separaten CALL-Befehl.

Beispiel für eine Prozedur mit Transaktionssteuerung

-- Prozedur mit schrittweisem Commit (funktioniert nur im Autocommit-Modus der Verbindung)
CREATE PROCEDURE bestellungen_im_batch_verarbeiten()
LANGUAGE plpgsql
AS $$
DECLARE
    rec RECORD;
BEGIN
    FOR rec IN SELECT bestell_id, bestellwert FROM eingehende_bestellungen LOOP
        BEGIN
            -- Jede Datencharge einzeln speichern
            INSERT INTO bestellungen (bestell_id, gesamt) VALUES (rec.bestell_id, rec.bestellwert);
        EXCEPTION WHEN OTHERS THEN
            INSERT INTO bestellungsfehler(bestell_id, fehler_text) VALUES (rec.bestell_id, SQLERRM);
        END;
        COMMIT;
    END LOOP;
END;
$$;

-- Prozedur aufrufen
CALL bestellungen_im_batch_verarbeiten();

Nach jedem COMMIT startet automatisch eine neue Transaktion.

Teilweiser Rollback (savepoint-ähnliches Verhalten) in PL/pgSQL

PL/pgSQL (sowohl in Funktionen als auch in Prozeduren) unterstützt nicht den Befehl ROLLBACK TO SAVEPOINT.

Um Änderungen in einem Teil des Codes zurückzusetzen, nutzt man nur den Block BEGIN ... EXCEPTION ... END:

BEGIN
    -- irgendwelche Aktionen
    BEGIN
        -- potenziell fehlerhafte Operation
    EXCEPTION WHEN OTHERS THEN
        -- alle Änderungen in diesem Block werden zurückgesetzt
        RAISE NOTICE 'Rollback im Block!';
    END;
END;

In Prozeduren kann man auch SAVEPOINT und RELEASE SAVEPOINT verwenden, aber nicht ROLLBACK TO SAVEPOINT. Der Sinn ist, Schritte zu trennen, aber steuern kann man sie nur über Exception-Handling.

Einschränkungen und Best Practices

  1. Funktionen – nur atomare Operationen: alles oder nichts. Wenn etwas schiefgeht, werden alle Änderungen zurückgesetzt.
  2. Prozeduren – nur per CALL: und nur als separater SQL-Befehl, nicht aus SELECT/Funktionen. Verschachtelte Transaktionssteuerung ist möglich, aber nur unter Einhaltung der PL/pgSQL-Einschränkungen.
  3. Teilweiser Rollback – nur über EXCEPTION: offiziell empfohlener und unterstützter Weg für teilweisen Rollback (wie SAVEPOINT).
  4. Verschachtelte Prozeduren können Transaktionen nur beim Aufruf per CALL steuern: sonst gibt es einen Fehler.

Fragen zum Zusammenspiel von Logik und Transaktionen

Kann ich eine „verschachtelte“ Transaktion innerhalb einer Funktion machen?

Nein. Alles läuft in einer Transaktion. Für teilweisen Rollback – nur EXCEPTION-Blöcke.

Kann ich COMMIT/ROLLBACK in einer Funktion oder einem anonymen Block machen?

Nein, das ist ein Syntaxfehler. Nutze Prozeduren dafür.

Kann man eine Prozedur aus einer Funktion aufrufen?

Nein, nur mit dem CALL-Befehl. Aus Funktion/SELECT – geht nicht.

Kann man in einer Prozedur ROLLBACK TO SAVEPOINT machen?

Nein! In PL/pgSQL ist das verboten. Nutze EXCEPTION-Blöcke.

Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION