Parę poziomów temu już gadaliśmy o procedurach i funkcjach w PostgreSQL. Teraz czas wejść w temat głębiej.
Funkcje i procedury mogą działać osobno, ale najczęściej to właśnie ich współpraca decyduje o sukcesie całego systemu. Największy plus jest taki, że funkcje można wywoływać jedna z drugiej, przekazując dane i nawet odbierając wynik działania.
Funkcje vs Procedury: jaka jest różnica?
Przypomnijmy sobie, czym funkcje różnią się od procedur w PostgreSQL:
Funkcje (
FUNCTION):- Zwracają wartości.
- Można ich używać w
SELECT. - Często służą do obliczeń albo przekształcania danych.
Procedury (
PROCEDURE):- Nie zwracają wartości bezpośrednio.
- Używane do operacji typu insert, update albo delete danych.
- Wywołuje się je przez komendę
CALL.
Przekazywanie danych między funkcjami
Przechodząc do praktyki, zaczniemy od prostego przykładu przekazywania danych między funkcją a procedurą. Generalnie, przekazywanie danych między funkcjami odbywa się przez parametry i zwracane wartości.
Tak wygląda wywołanie funkcji w środku innej funkcji:
CREATE OR REPLACE FUNCTION get_student_name(student_id INT)
RETURNS TEXT AS $$
DECLARE
student_name TEXT;
BEGIN
-- Pobieramy imię studenta po jego ID
SELECT name INTO student_name FROM students WHERE id = student_id;
-- Zwracamy imię
RETURN student_name;
END;
$$ LANGUAGE plpgsql;
Tę funkcję można wywołać z innej funkcji:
CREATE OR REPLACE FUNCTION welcome_student(student_id INT)
RETURNS TEXT AS $$
DECLARE
message TEXT;
BEGIN
-- Pobieramy imię studenta przez inną funkcję
message := 'Witaj, ' || get_student_name(student_id) || '!';
-- Zwracamy powitanie
RETURN message;
END;
$$ LANGUAGE plpgsql;
- Funkcja
get_student_namezwraca imię studenta na podstawie jego identyfikatora (student_id). - W drugiej funkcji —
welcome_student— to imię jest używane do stworzenia powitania.
Uwaga: Pobieranie danych przez SELECT INTO zapisuje wynik zapytania do zmiennej PL/pgSQL.
Przykład wywołania procedur z funkcji
Teraz zobaczmy, jak wywołać procedurę z funkcji. Załóżmy, że mamy procedurę, która zapisuje czas wejścia studenta do systemu:
CREATE OR REPLACE PROCEDURE log_student_entry(student_id INT)
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO log_entries(student_id, entry_time)
VALUES (student_id, NOW());
END;
$$;
Teraz wywołamy tę procedurę z funkcji, gdzie będzie ona logować wejście i zwracać komunikat:
CREATE OR REPLACE FUNCTION student_login(student_id INT)
RETURNS TEXT AS $$
BEGIN
-- Wywołujemy procedurę do logowania
CALL log_student_entry(student_id);
-- Zwracamy komunikat
RETURN 'Logowanie studenta zapisane poprawnie.';
END;
$$ LANGUAGE plpgsql;
Praktyczne przykłady współpracy
Przykład 1: obliczanie sumy końcowej i logowanie zamówienia
Wyobraź sobie, że pracujesz z systemem zamówień online. Do obliczania sumy końcowej zamówienia masz funkcję:
CREATE OR REPLACE FUNCTION calculate_order_total(order_id INT)
RETURNS NUMERIC AS $$
DECLARE
total NUMERIC;
BEGIN
-- Sumujemy wszystkie pozycje zamówienia
SELECT SUM(price * quantity) INTO total
FROM order_items
WHERE order_id = order_id;
RETURN total;
END;
$$ LANGUAGE plpgsql;
Do zapisania sumy końcowej zamówienia używasz procedury:
CREATE OR REPLACE PROCEDURE log_order_total(order_id INT, total NUMERIC)
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO order_totals(order_id, total)
VALUES (order_id, total);
END;
$$;
Teraz połączmy je razem:
CREATE OR REPLACE FUNCTION process_order(order_id INT)
RETURNS TEXT AS $$
DECLARE
total NUMERIC;
BEGIN
-- Wywołujemy funkcję do obliczenia sumy końcowej
total := calculate_order_total(order_id);
-- Logujemy sumę końcową przez procedurę
CALL log_order_total(order_id, total);
RETURN 'Zamówienie przetworzone poprawnie.';
END;
$$ LANGUAGE plpgsql;
Przykład 2: pobieranie maksymalnego ratingu studenta i aktualizacja profilu
Funkcja do pobierania maksymalnego ratingu:
CREATE OR REPLACE FUNCTION get_highest_rating(student_id INT)
RETURNS INT AS $$
DECLARE
max_rating INT;
BEGIN
-- Szukamy maksymalnego ratingu studenta
SELECT MAX(rating) INTO max_rating
FROM ratings
WHERE student_id = student_id;
RETURN max_rating;
END;
$$ LANGUAGE plpgsql;
Procedura do aktualizacji profilu studenta:
CREATE OR REPLACE PROCEDURE update_student_profile(student_id INT, max_rating INT)
LANGUAGE plpgsql
AS $$
BEGIN
UPDATE students
SET highest_rating = max_rating
WHERE id = student_id;
END;
$$;
Funkcja do wywołania tych operacji:
CREATE OR REPLACE FUNCTION refresh_student_profile(student_id INT)
RETURNS TEXT AS $$
DECLARE
max_rating INT;
BEGIN
-- Pobieramy maksymalny rating
max_rating := get_highest_rating(student_id);
-- Aktualizujemy profil studenta
CALL update_student_profile(student_id, max_rating);
RETURN 'Profil zaktualizowany poprawnie.';
END;
$$ LANGUAGE plpgsql;
Typowe błędy przy współpracy
Jeden z najczęstszych błędów — niezgodność typów danych między funkcją a procedurą. Na przykład, jeśli twoja procedura oczekuje parametru typu NUMERIC, a ty podasz INTEGER, PostgreSQL zgłosi błąd typu. Zawsze sprawdzaj, czy typy danych się zgadzają.
Kolejny błąd to cykliczne wywoływanie funkcji, gdy funkcja A wywołuje funkcję B, a ta znowu A. To prowadzi do nieskończonego wywoływania i zawieszenia systemu.
Praktyczne znaczenie
Po co nam taka współpraca? W realu funkcje i procedury działają jak "klocki" z których budujesz złożone systemy. Pozwalają podzielić kod na niezależne części, co ułatwia debugowanie, ponowne użycie i testowanie. Na przykład:
- Na rozmowie kwalifikacyjnej mogą cię poprosić o napisanie funkcji, która wywołuje procedurę do wykonania złożonej operacji. Pokazanie praktycznych umiejętności współpracy to duży plus.
- Przy tworzeniu prawdziwych aplikacji, takich jak sklepy internetowe, systemy logowania czy CRM, umiejętność sensownego organizowania funkcjonalności przez współpracę funkcji i procedur bardzo upraszcza kod.
Żeby jeszcze lepiej ogarnąć współpracę funkcji i procedur, możesz zajrzeć do oficjalnej dokumentacji PL/pgSQL.
GO TO FULL VERSION