CodeGym /Kursy /SQL SELF /Analiza typowych błędów podczas debugowania i optymalizac...

Analiza typowych błędów podczas debugowania i optymalizacji PL/pgSQL

SQL SELF
Poziom 56, Lekcja 4
Dostępny

Dzisiaj, żeby zakończyć tę epicką podróż przez PL/pgSQL, przeanalizujemy najczęstsze błędy, które mogą cię dopaść podczas debugowania i optymalizacji funkcji oraz procedur. Znajomość tych błędów pomoże ci nie tylko unikać problemów w przyszłości, ale też szybciej ogarniać bugi, jeśli jednak się pojawią.

Typowe błędy podczas debugowania i optymalizacji

1. Nieprawidłowe użycie zmiennych

Jednym z najczęstszych błędów podczas pisania i debugowania funkcji w PL/pgSQL jest złe zadeklarowanie lub użycie zmiennych. Na przykład, jeśli zapomnisz jawnie określić typ zmiennej albo pogubisz się w wartościach przekazywanych przez parametry. Zobaczmy, jak to może wyglądać w praktyce:

CREATE OR REPLACE FUNCTION calculate_discount(order_total NUMERIC)
RETURNS NUMERIC AS $$
DECLARE
    discount_rate NUMERIC;
BEGIN
    -- Ups! Zapomniałem zainicjalizować zmienną discount_rate
    RETURN order_total * discount_rate;
END;
$$ LANGUAGE plpgsql;

Wywołując tę funkcję dostaniesz błąd związany z użyciem NULL w obliczeniach, bo zmienna discount_rate na starcie nie jest zainicjalizowana.

Jak tego uniknąć:

  1. Zawsze przypisuj zmiennym wartości domyślne podczas deklaracji:
   DECLARE
       discount_rate NUMERIC := 0.1; -- Wartość domyślna
  1. Sprawdzaj wartości zmiennych za pomocą RAISE NOTICE, żeby mieć pewność, że mają to, czego oczekujesz:
RAISE NOTICE 'Wartość discount_rate: %', discount_rate;

2. Brak logowania błędów

Kolejny popularny problem to brak mechanizmu logowania. Jeśli coś pójdzie nie tak, a nie logujesz przebiegu swojej funkcji, to jak szukanie czarnego kota w ciemnym pokoju, zwłaszcza jeśli nie masz pewności, czy ten kot tam w ogóle jest.

Oto przykład funkcji bez logowania:

CREATE OR REPLACE FUNCTION process_order(order_id INT)
RETURNS VOID AS $$
BEGIN
    -- Jakaś skomplikowana logika obsługi zamówienia
    UPDATE orders SET status = 'processed' WHERE id = order_id;
END;
$$ LANGUAGE plpgsql;

A co jeśli order_id jest przekazany niepoprawnie? Albo jeśli rekord w tabeli orders nie istnieje?

Jak tego uniknąć: Dodaj RAISE NOTICE albo RAISE EXCEPTION, żeby logować kluczowe kroki:

CREATE OR REPLACE FUNCTION process_order(order_id INT)
RETURNS VOID AS $$
BEGIN
    -- Logujemy dane wejściowe
    RAISE NOTICE 'Obsługa zamówienia z ID %', order_id;

    -- Skomplikowana logika obsługi
    UPDATE orders SET status = 'processed' WHERE id = order_id;

    -- Logujemy wynik
    RAISE NOTICE 'Status zamówienia zaktualizowany dla ID %', order_id;
END;
$$ LANGUAGE plpgsql;

Teraz łatwo namierzyć, gdzie pojawia się błąd, dzięki wyświetlanym komunikatom.

3. Ignorowanie wydajności zapytań

To jeden z głównych wrogów każdego developera baz danych. Na przykład, piszesz funkcję, która wygląda OK, ale działa strasznie wolno. A jedną z głównych przyczyn wolnych zapytań jest brak indeksów albo nieefektywne plany wykonania zapytań.

Przykład wolnego zapytania:

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;

Jeśli pole total w tabeli orders nie jest zindeksowane, zapytanie przeskanuje całą tabelę, co jest mega nieefektywne.

Jak tego uniknąć:

  1. Używaj EXPLAIN ANALYZE, żeby sprawdzić wydajność zapytań:
EXPLAIN ANALYZE SELECT id, total FROM orders WHERE total > 1000;
  1. Twórz indeksy na często używanych kolumnach:
CREATE INDEX idx_orders_total ON orders(total);

4. Użycie złego poziomu izolacji transakcji

Podczas wykonywania złożonych procedur czasem pojawiają się błędy przez złe zrozumienie poziomów izolacji transakcji. Na przykład, jeśli dwie transakcje jednocześnie próbują zaktualizować ten sam rekord, może dojść do deadlock.

Przykład potencjalnego deadlock:

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

-- Oczekiwanie na blokadę innej transakcji
UPDATE inventory SET stock = stock - 1 WHERE product_id = 100;
COMMIT;

Jeśli inna transakcja wykona te operacje w innej kolejności, dostaniesz wzajemną blokadę.

Jak tego uniknąć:

  1. Przemyśl kolejność wykonywania operacji i trzymaj się jej.
  2. Używaj poziomu izolacji SERIALIZABLE, jeśli to konieczne.

5. Brak obsługi błędów

Obsługa błędów to nie tylko dobra praktyka, ale też narzędzie do zapewnienia stabilności twojego kodu. Na przykład, w poniższym kodzie nie ma obsługi potencjalnych błędów:

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;

Jeśli nagle order_id już istnieje, dostaniesz błąd duplicate key value violates unique constraint.

Jak tego uniknąć: Użyj bloków obsługi wyjątków:

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 'Zamówienie z ID % już istnieje!', order_id;
END;
$$ LANGUAGE plpgsql;

Przykłady błędów i ich poprawa

Błąd 1: Zapytania działają wolno przez brak indeksowania

Sytuacja: Masz zapytanie, które filtruje tabelę po kolumnie, ale ta kolumna nie ma indeksu.

Poprawa: Utwórz indeks dla odpowiedniej kolumny.

Błąd 2: Logika funkcji jest zagmatwana i trudno ją debugować

Sytuacja: Funkcja zawiera za dużo logiki i nie jest podzielona na podfunkcje.

Poprawa: Podziel złożoną funkcję na mniejsze podfunkcje. To poprawi czytelność i ułatwi debugowanie.

Błąd 3: Nieprawidłowe użycie RAISE EXCEPTION

Sytuacja: RAISE EXCEPTION jest używane do wszystkich błędów, nawet tych mało istotnych.

Poprawa: Używaj RAISE NOTICE do komunikatów informacyjnych, a RAISE EXCEPTION tylko do krytycznych przypadków.

RAISE NOTICE 'Wszystko pod kontrolą — bieżący etap funkcji zakończony.';
RAISE EXCEPTION 'Coś się zepsuło! Sprawdź parametry wejściowe.';

Wskazówki jak zapobiegać błędom

  1. Dodawaj logowanie: na kluczowych etapach funkcji używaj RAISE NOTICE do śledzenia przebiegu.
  2. Testuj funkcje: regularnie korzystaj z danych testowych do sprawdzania funkcji i procedur.
  3. Dbaj o czytelność kodu: dziel złożone funkcje na mniejsze podfunkcje i procedury.
  4. Analizuj wydajność: używaj EXPLAIN ANALYZE, żeby mieć pewność, że twoje zapytania działają sprawnie.
  5. Bądź gotowy na niespodzianki: zawsze dodawaj bloki obsługi wyjątków do obsługi błędów.
EXCEPTION
    WHEN OTHERS THEN
        RAISE EXCEPTION 'Wystąpił nieoczekiwany błąd: %', SQLERRM;

Dzięki temu pewniej ogarniesz błędy, a także zapobiegniesz ich pojawianiu się w przyszłości.

2
Zadanie
SQL SELF, poziom 56, lekcja 4
Niedostępne
Obsługa błędów przy duplikującym się ID
Obsługa błędów przy duplikującym się ID
1
Ankieta/quiz
Optymalizacja funkcji, poziom 56, lekcja 4
Niedostępny
Optymalizacja funkcji
Optymalizacja funkcji
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION