CodeGym /Kurse /SQL SELF /Typische Fehler beim Debuggen und Optimieren von PL/pgSQL...

Typische Fehler beim Debuggen und Optimieren von PL/pgSQL – Analyse

SQL SELF
Level 56 , Lektion 4
Verfügbar

Heute, um diese epische Reise durch PL/pgSQL abzuschließen, schauen wir uns die häufigsten Fehler an, die dir beim Debuggen und Optimieren von Funktionen und Prozeduren begegnen können. Wenn du diese Fehler kennst, kannst du nicht nur Probleme in Zukunft vermeiden, sondern auch Bugs schneller finden, falls sie doch mal auftauchen.

Typische Fehler beim Debuggen und Optimieren

1. Falsche Verwendung von Variablen

Einer der häufigsten Fehler beim Schreiben und Debuggen von Funktionen in PL/pgSQL ist das falsche Deklarieren oder Benutzen von Variablen. Zum Beispiel, wenn du vergisst, den Typ einer Variable explizit anzugeben oder durcheinander kommst mit den Werten, die über Parameter reinkommen. Schauen wir uns mal an, wie das in der Praxis aussehen kann:

CREATE OR REPLACE FUNCTION calculate_discount(order_total NUMERIC)
RETURNS NUMERIC AS $$
DECLARE
    discount_rate NUMERIC;
BEGIN
    -- Ups! Variable discount_rate wurde nicht initialisiert
    RETURN order_total * discount_rate;
END;
$$ LANGUAGE plpgsql;

Wenn du diese Funktion aufrufst, bekommst du einen Fehler, weil du NULL in Berechnungen verwendest – die Variable discount_rate ist nämlich am Anfang nicht initialisiert.

Wie du das vermeidest:

  1. Gib Variablen immer einen Default-Wert beim Deklarieren:
   DECLARE
       discount_rate NUMERIC := 0.1; -- Standardwert
  1. Checke Variablen mit RAISE NOTICE, um sicherzugehen, dass sie die erwarteten Werte haben:
RAISE NOTICE 'Wert von discount_rate: %', discount_rate;

2. Fehlendes Fehler-Logging

Ein weiteres typisches Problem ist fehlendes Logging. Wenn etwas schief läuft und du loggst nicht, was in deiner Funktion passiert, ist das wie eine schwarze Katze im dunklen Raum zu suchen – besonders, wenn du nicht mal sicher bist, ob da überhaupt eine Katze ist.

Hier ein Beispiel für eine Funktion ohne Logging:

CREATE OR REPLACE FUNCTION process_order(order_id INT)
RETURNS VOID AS $$
BEGIN
    -- Irgendeine komplexe Logik zur Bestellverarbeitung
    UPDATE orders SET status = 'processed' WHERE id = order_id;
END;
$$ LANGUAGE plpgsql;

Was, wenn order_id falsch übergeben wird? Was, wenn der Datensatz in der Tabelle orders gar nicht existiert?

Wie du das vermeidest: Füge RAISE NOTICE oder RAISE EXCEPTION hinzu, um kritische Schritte zu loggen:

CREATE OR REPLACE FUNCTION process_order(order_id INT)
RETURNS VOID AS $$
BEGIN
    -- Eingabedaten loggen
    RAISE NOTICE 'Bestellung mit ID % wird verarbeitet', order_id;

    -- Komplexe Logik
    UPDATE orders SET status = 'processed' WHERE id = order_id;

    -- Ergebnis loggen
    RAISE NOTICE 'Status der Bestellung wurde aktualisiert für ID %', order_id;
END;
$$ LANGUAGE plpgsql;

Jetzt kannst du easy nachvollziehen, wo ein Fehler auftritt, weil du die Ausgaben siehst.

3. Performance von Abfragen ignorieren

Das ist einer der größten Endgegner für jeden Datenbankentwickler. Du schreibst eine Funktion, die eigentlich korrekt aussieht, aber sie läuft mega langsam. Einer der Hauptgründe für langsame Abfragen ist das Fehlen von Indizes oder schlechte Ausführungspläne.

Beispiel für eine langsame Abfrage:

CREATE OR REPLACE FUNCTION get_large_orders()
RETURNS TABLE(order_id INT, total NUMERIC) AS $$
BEGIN
    RETURN QUERY
    SELECT id, total FROM orders WHERE total > 1000;
END;
$$ LANGUAGE plpgsql;

Wenn das Feld total in der Tabelle orders nicht indiziert ist, wird die ganze Tabelle gescannt – das ist super ineffizient.

Wie du das vermeidest:

  1. Benutze EXPLAIN ANALYZE, um zu checken, wie effizient deine Abfragen sind:
EXPLAIN ANALYZE SELECT id, total FROM orders WHERE total > 1000;
  1. Erstelle Indizes auf Spalten, die oft verwendet werden:
CREATE INDEX idx_orders_total ON orders(total);

4. Falsches Transaktions-Isolationslevel verwenden

Bei komplexen Prozeduren gibt es manchmal Fehler, weil das Isolationslevel der Transaktion nicht richtig verstanden wird. Wenn zum Beispiel zwei Transaktionen gleichzeitig versuchen, denselben Datensatz zu aktualisieren, kann das zu einem deadlock führen.

Beispiel für einen möglichen deadlock:

BEGIN;
UPDATE orders SET status = 'processed' WHERE id = 1;

-- Warten auf Sperre einer anderen Transaktion
UPDATE inventory SET stock = stock - 1 WHERE product_id = 100;
COMMIT;

Wenn eine andere Transaktion diese Operationen in anderer Reihenfolge macht, gibt’s eine gegenseitige Blockade.

Wie du das vermeidest:

  1. Überlege dir die Reihenfolge der Operationen und halte dich daran.
  2. Benutze das Isolationslevel SERIALIZABLE, wenn nötig.

5. Fehlende Fehlerbehandlung

Fehlerbehandlung ist nicht nur Best Practice, sondern auch ein Tool, um deinen Code stabil zu halten. Im folgenden Code fehlt zum Beispiel die Behandlung möglicher Fehler:

CREATE OR REPLACE FUNCTION add_order(order_id INT)
RETURNS VOID AS $$
BEGIN
    INSERT INTO orders (id, status) VALUES (order_id, 'new');
END;
$$ LANGUAGE plpgsql;

Wenn order_id schon existiert, bekommst du den Fehler duplicate key value violates unique constraint.

Wie du das vermeidest: Benutze Exception-Handling-Blöcke:

CREATE OR REPLACE FUNCTION add_order(order_id INT)
RETURNS VOID AS $$
BEGIN
    INSERT INTO orders (id, status) VALUES (order_id, 'new');
EXCEPTION WHEN unique_violation THEN
    RAISE NOTICE 'Bestellung mit ID % existiert schon!', order_id;
END;
$$ LANGUAGE plpgsql;

Beispiele für Fehler und deren Behebung

Fehler 1: Abfragen sind langsam, weil Indizes fehlen

Situation: Du hast eine Abfrage, die eine Tabelle nach einer Spalte filtert, aber für diese Spalte gibt’s keinen Index.

Behebung: Erstelle einen Index für die entsprechende Spalte.

Fehler 2: Die Logik der Funktion ist verwirrend und schwer zu debuggen

Situation: Die Funktion enthält zu viel Logik und ist nicht in Unterfunktionen aufgeteilt.

Behebung: Teile die komplexe Funktion in kleinere Unterfunktionen auf. Das macht den Code lesbarer und das Debuggen einfacher.

Fehler 3: Falsche Verwendung von RAISE EXCEPTION

Situation: RAISE EXCEPTION wird für alle Fehler benutzt, auch für unwichtige.

Behebung: Benutze RAISE NOTICE für Infos und RAISE EXCEPTION nur für kritische Fälle.

RAISE NOTICE 'Alles im Griff – aktueller Schritt der Funktion ist fertig.';
RAISE EXCEPTION 'Irgendwas ist kaputt! Check die Eingabeparameter.';

Tipps zur Fehlervermeidung

  1. Füge Logging hinzu: Nutze RAISE NOTICE an kritischen Stellen deiner Funktion, um den Ablauf zu verfolgen.
  2. Teste deine Funktionen: Nutze regelmäßig Testdaten, um Funktionen und Prozeduren zu prüfen.
  3. Halte den Code lesbar: Teile komplexe Funktionen in kleinere Unterfunktionen und Prozeduren auf.
  4. Analysiere die Performance: Nutze EXPLAIN ANALYZE, um sicherzugehen, dass deine Abfragen effizient laufen.
  5. Sei auf Überraschungen vorbereitet: Baue immer Exception-Handling-Blöcke ein, um mit Fehlern umzugehen.
EXCEPTION
    WHEN OTHERS THEN
        RAISE EXCEPTION 'Unerwarteter Fehler aufgetreten: %', SQLERRM;

So kannst du Fehler easy finden und verhinderst, dass sie in Zukunft wieder auftauchen.

1
Umfrage/Quiz
Funktionsoptimierung, Level 56, Lektion 4
Nicht verfügbar
Funktionsoptimierung
Funktionsoptimierung
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION