Пару уровней назад мы уже поднимали вопрос процедур и функций в PostgreSQL. Пришло время изучить их глубже.
Функции и процедуры могут работать независимо, но чаще всего их взаимодействие определяет успех всей системы. Главное удобство в том, что функции можно вызывать одну из другой, передавая данные и даже получая результат выполнения.
Функции vs Процедуры: в чем разница?
Давайте вспомним, чем функции отличаются от процедур в PostgreSQL:
Функции (
FUNCTION):- Возвращают значения.
- Их можно использовать в
SELECT. - Часто применяются для вычислений или преобразования данных.
Процедуры (
PROCEDURE):- Не возвращают значений напрямую.
- Используются для выполнения операций, таких как вставка, обновление или удаление данных.
- Вызываются с помощью команды
CALL.
Передача данных между функциями
Переходя к практике, начнем с базового примера передачи данных между функцией и процедурой. По сути, передача данных между функциями осуществляется через параметры и возвращаемые значения.
Вот как выглядит вызов функции внутри другой функции:
CREATE OR REPLACE FUNCTION get_student_name(student_id INT)
RETURNS TEXT AS $$
DECLARE
student_name TEXT;
BEGIN
-- Извлекаем имя студента по его ID
SELECT name INTO student_name FROM students WHERE id = student_id;
-- Возвращаем имя
RETURN student_name;
END;
$$ LANGUAGE plpgsql;
Эту функцию можно вызывать из другой функции:
CREATE OR REPLACE FUNCTION welcome_student(student_id INT)
RETURNS TEXT AS $$
DECLARE
message TEXT;
BEGIN
-- Получаем имя студента с помощью другой функции
message := 'Welcome, ' || get_student_name(student_id) || '!';
-- Возвращаем приветствие
RETURN message;
END;
$$ LANGUAGE plpgsql;
- Функция
get_student_nameвозвращает имя студента по его идентификатору (student_id). - В другой функции —
welcome_student— это имя используется для создания приветственного сообщения.
Примечание: Извлечение данных через SELECT INTO сохраняет результат запроса в переменной PL/pgSQL.
Пример вызова процедур из функций
Теперь давайте рассмотрим, как вызывать процедуру из функции. Предположим, у нас есть процедура, которая фиксирует время входа студента в систему:
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;
$$;
Теперь вызовем эту процедуру из функции, где она будет фиксировать вход и возвращать сообщение:
CREATE OR REPLACE FUNCTION student_login(student_id INT)
RETURNS TEXT AS $$
BEGIN
-- Вызываем процедуру для логирования
CALL log_student_entry(student_id);
-- Возвращаем сообщение
RETURN 'Student login logged successfully.';
END;
$$ LANGUAGE plpgsql;
Практические примеры взаимодействия
Пример 1: расчёт итоговой суммы и логирование заказа
Представьте, что вы работаете с системой онлайн-заказов. Для расчёта итоговой суммы заказа у вас есть функция:
CREATE OR REPLACE FUNCTION calculate_order_total(order_id INT)
RETURNS NUMERIC AS $$
DECLARE
total NUMERIC;
BEGIN
-- Суммируем все позиции заказа
SELECT SUM(price * quantity) INTO total
FROM order_items
WHERE order_id = order_id;
RETURN total;
END;
$$ LANGUAGE plpgsql;
Для сохранения итоговой суммы заказа используется процедура:
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;
$$;
Теперь свяжем их вместе:
CREATE OR REPLACE FUNCTION process_order(order_id INT)
RETURNS TEXT AS $$
DECLARE
total NUMERIC;
BEGIN
-- Вызываем функцию для расчета итоговой суммы
total := calculate_order_total(order_id);
-- Логируем итоговую сумму через процедуру
CALL log_order_total(order_id, total);
RETURN 'Order processed successfully.';
END;
$$ LANGUAGE plpgsql;
Пример 2: получение максимального рейтинга студента и обновление профиля
Функция для получения максимального рейтинга:
CREATE OR REPLACE FUNCTION get_highest_rating(student_id INT)
RETURNS INT AS $$
DECLARE
max_rating INT;
BEGIN
-- Находим максимальный рейтинг студента
SELECT MAX(rating) INTO max_rating
FROM ratings
WHERE student_id = student_id;
RETURN max_rating;
END;
$$ LANGUAGE plpgsql;
Процедура для обновления профиля студента:
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;
$$;
Функция для вызова этих операций:
CREATE OR REPLACE FUNCTION refresh_student_profile(student_id INT)
RETURNS TEXT AS $$
DECLARE
max_rating INT;
BEGIN
-- Получаем максимальный рейтинг
max_rating := get_highest_rating(student_id);
-- Обновляем профиль студента
CALL update_student_profile(student_id, max_rating);
RETURN 'Profile updated successfully.';
END;
$$ LANGUAGE plpgsql;
Типичные ошибки при взаимодействии
Одна из самых распространённых ошибок — несоответствие типов данных между функцией и процедурой. Например, если ваша процедура ожидает параметр типа NUMERIC, а вы передаете INTEGER, PostgreSQL сообщит о несоответствии типов. Всегда проверяйте, что типы данных совпадают.
Еще одна ошибка заключается в циклическом вызове функций, когда функция A вызывает функцию B, а та, в свою очередь, снова вызывает A. Это приводит к бесконечному вызову и краху системы.
Практическая значимость
Зачем нам нужно такое взаимодействие? В реальной жизни функции и процедуры работают как "строительные блоки" сложных систем. Они позволяют разбить код на независимые части, что облегчает отладку, повторное использование и тестирование. Например:
- На собеседовании вас могут попросить написать функцию, которая вызывает процедуру для выполнения сложной операции. Демонстрация практических навыков взаимодействия будет большим плюсом.
- При разработке реальных приложений, таких как интернет-магазины, системы логирования или CRM, умение правильно организовать функциональность через взаимодействие функций и процедур значительно упрощает код.
Для дальнейшего изучения взаимодействия функций и процедур вы можете изучить официальную документацию по PL/pgSQL.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ