В предыдущем материале мы разбирали представления (views) — инструмент для упрощения сложных запросов. Сегодня сделаем следующий важный шаг и познакомимся с хранимыми функциями. Если представления можно сравнить с виртуальными таблицами, то функции — это полноценные программные модули, которые выполняют вычисления и возвращают результат прямо на стороне сервера баз данных.
Что такое хранимые функции?
Хранимая функция — это объект базы данных, который содержит набор SQL-инструкций, скомпилированных и сохранённых на сервере. Функции принимают параметры, выполняют логику и возвращают результат.
Ключевые особенности:
-
Возвращают значение (число, строку, таблицу и т.д.)
-
Могут использоваться в операторах SELECT
-
Инкапсулируют сложную логику на стороне БД
-
Переиспользуются в разных приложениях и запросах
-
Повышают безопасность (пользователь получает доступ к функции, но не к исходным таблицам)
Пример 1: Функция для подсчёта сотрудников по должности
Задача
Создать функцию, которая принимает название должности и возвращает количество активных сотрудников, занимающих эту должность в текущий момент.
Код функции
CREATE OR REPLACE FUNCTION get_employee_count_by_position(pos_name VARCHAR) RETURNS INTEGER AS $$ DECLARE employee_count INTEGER; BEGIN SELECT COUNT(DISTINCT e.id) INTO employee_count FROM employees e JOIN positions_by_date pbd ON e.id = pbd.employee_id JOIN positions p ON pbd.position_id = p.id WHERE p.position_name = pos_name AND pbd.date_start <= CURRENT_DATE AND (pbd.date_finish IS NULL OR pbd.date_finish > CURRENT_DATE) AND e.is_active = true AND p.is_active = true; RETURN employee_count; END; $$ LANGUAGE plpgsql;
Разбор структуры
| Элемент | Назначение |
|---|---|
CREATE OR REPLACE FUNCTION |
Создаёт новую функцию или заменяет существующую |
pos_name VARCHAR |
Входной параметр — название должности |
RETURNS INTEGER |
Тип возвращаемого значения |
DECLARE |
Блок объявления переменных |
employee_count INTEGER |
Переменная для хранения результата |
SELECT ... INTO |
Запрос, результат которого сохраняется в переменную |
RETURN |
Возврат результата |
LANGUAGE plpgsql |
Язык написания функции (процедурный SQL) |
Логика запроса внутри функции
Запрос объединяет три таблицы:
-
employees — данные о сотрудниках
-
positions_by_date — связь сотрудников с должностями по периодам
-
positions — справочник должностей
Условия фильтрации:
-
Должность соответствует переданному названию
-
Период работы актуален на текущую дату
-
Сотрудник активен
-
Должность активна
Вызов функции
-- Сколько менеджеров в компании? SELECT get_employee_count_by_position('Менеджер');
Результат: 0 (в компании нет активных менеджеров)
-- Сколько бухгалтеров? SELECT get_employee_count_by_position('Бухгалтер');
Результат: 1 (один активный бухгалтер)
Преимущество: вместо написания сложного JOIN-запроса каждый раз мы просто вызываем функцию с нужным параметром.
Пример 2: Функция, возвращающая таблицу с историей сотрудника
Задача
Создать функцию, которая принимает ID сотрудника и возвращает полную историю его карьерных перемещений: должности, даты назначения и увольнения, продолжительность работы.
Код функции
CREATE OR REPLACE FUNCTION get_employee_career_history(emp_id INTEGER) RETURNS TABLE ( full_name TEXT, position_name VARCHAR, start_date DATE, finish_date DATE, duration_days INTEGER ) AS $$ BEGIN RETURN QUERY SELECT CONCAT(e.last_name, ' ', e.first_name) AS full_name, p.position_name, pbd.date_start, pbd.date_finish, COALESCE(pbd.date_finish, CURRENT_DATE) - pbd.date_start AS duration_days FROM employees e JOIN positions_by_date pbd ON e.id = pbd.employee_id JOIN positions p ON pbd.position_id = p.id WHERE e.id = emp_id ORDER BY pbd.date_start DESC; END; $$ LANGUAGE plpgsql;
Ключевые отличия от первой функции
-
RETURNS TABLE — функция возвращает не скалярное значение, а таблицу
-
Явное описание структуры — перечислены все поля с типами
-
RETURN QUERY — специальная конструкция, наполняющая результирующую таблицу данными из запроса
-
Расчёт продолжительности —
COALESCE(date_finish, CURRENT_DATE) - date_startдаёт количество дней (если дата окончания не указана, используется текущая дата)
Вызов функции
SELECT * FROM get_employee_career_history(1);
Результат:
| full_name | position_name | start_date | finish_date | duration_days |
|---|---|---|---|---|
| Иванов Иван | Директор | 2020-01-15 | 2099-12-31 | 29155 |
| Иванов Иван | Зам. директора | 2018-03-10 | 2020-01-14 | 675 |
| Иванов Иван | Менеджер | 2015-06-01 | 2018-03-09 | 1012 |
Что мы видим:
-
Полная история карьерного роста
-
Автоматический расчёт продолжительности в днях
-
Для текущей должности указана конечная дата 2099 год (условное обозначение «бессрочно»)
-
Сортировка от новых к старым позициям
Преимущества использования хранимых функций
1. Инкапсуляция сложности
Вся логика работы с данными скрыта внутри функции. Приложению не нужно знать структуру таблиц и детали JOIN-объединений.
2. Переиспользование
Одну и ту же функцию можно вызывать из разных частей приложения, из отчётов, из административных скриптов.
3. Согласованность
Любые изменения в логике расчётов вносятся в одном месте — в теле функции. Все вызывающие стороны автоматически получают обновлённое поведение.
4. Производительность
Функции хранятся в скомпилированном виде, план выполнения оптимизируется, сетевой трафик сокращается (передаётся только результат, а не промежуточные данные).
5. Безопасность
Можно дать пользователю право вызывать функцию, но не давать прямого доступа к таблицам.
Где найти созданные функции?
В среде pgAdmin все хранимые функции доступны в навигаторе:
-
Найдите вашу базу данных
-
Раскройте раздел Schemas → public
-
Найдите папку Functions
-
При необходимости обновите список через Refresh
Практические рекомендации
Когда стоит создавать функцию?
✅ Сложная логика, используемая в нескольких местах
✅ Расчёты, требующие временных таблиц или циклов
✅ Бизнес-правила, которые должны применяться единообразно
✅ Операции, которые лучше выполнять на сервере (ближе к данным)
Чего избегать?
❌ Слишком простых операций, которые легко написать в запросе
❌ Функций, выполняющих тяжелые вычисления на больших объёмах данных без индексов
❌ Избыточного количества параметров (больше 3-4)
Дальнейшие шаги
В этом уроке мы рассмотрели два типа функций:
-
Скалярные функции — возвращают одно значение (число, строку)
-
Табличные функции — возвращают набор записей
Следующий уровень — хранимые процедуры, которые в отличие от функций не обязаны возвращать значение и могут управлять транзакциями. Но это тема отдельного разговора.
Заключение
Хранимые функции в PostgreSQL — это мощный инструмент для выноса бизнес-логики на уровень базы данных. Они делают код приложений чище, повышают производительность и обеспечивают согласованность данных. Освоив создание и использование функций, вы подниметесь на новую ступень в работе с PostgreSQL.
Попробуйте самостоятельно:
-
Создать функцию для подсчёта количества сотрудников в подчинении у конкретного менеджера
-
Написать функцию, возвращающую среднюю продолжительность работы в компании по должностям
-
Модифицировать пример с историей, добавив фильтр по периоду
Хотите увидеть процесс создания и отладки хранимых функций в реальном времени — с разбором типичных ошибок, советами по оптимизации и демонстрацией работы в pgAdmin? В нашем видео-руководстве мы:
-
Пошагово создаём обе функции из этого урока
-
Разбираем каждую строку кода
-
Показываем, как отлаживать функции при возникновении ошибок
-
Демонстрируем реальные примеры вызова с разными параметрами
-
Объясняем, как читать планы выполнения для функций
Статья подготовлена по материалам обучающего видео для вашего удобства.