Что такое хранимые функции в PostgreSQL?

MiddlePostgreSQL · Backend·Обновлено 16 июля 2026
Коротко
Хранимые функции в PostgreSQL — это именованные блоки кода на PL/pgSQL (или другом языке), которые хранятся в базе данных и могут принимать параметры, выполнять логику и возвращать результат. В отличие от хранимых процедур, функции обязательно возвращают значение.

Хранимые функции в PostgreSQL

Хранимая функция (stored function) — это объект базы данных, содержащий именованный блок кода, который компилируется и сохраняется на сервере. Функции можно вызывать из SQL-запросов, триггеров или других функций.

Основные характеристики

  • Хранятся в системном каталоге базы данных
  • Выполняются на стороне сервера, снижая сетевой трафик
  • Могут быть написаны на PL/pgSQL, SQL, PL/Python, PL/V8 и других языках
  • Обязательно возвращают значение (в отличие от процедур PROCEDURE)
  • Поддерживают перегрузку — несколько функций с одним именем, но разными параметрами

Синтаксис создания

CREATE OR REPLACE FUNCTION имя_функции(параметры)
RETURNS тип_возврата
LANGUAGE plpgsql
AS $$
DECLARE
  -- объявление переменных
BEGIN
  -- тело функции
  RETURN значение;
END;
$$;

Типы возвращаемых значений

Скалярные значения — функция возвращает одно значение:

CREATE OR REPLACE FUNCTION get_user_age(user_id INT)
RETURNS INT
LANGUAGE plpgsql
AS $$
DECLARE
  v_age INT;
BEGIN
  SELECT age INTO v_age FROM users WHERE id = user_id;
  RETURN v_age;
END;
$$;

RETURNS TABLE — функция возвращает набор строк:

CREATE OR REPLACE FUNCTION get_active_users(min_age INT)
RETURNS TABLE(id INT, name TEXT, email TEXT)
LANGUAGE plpgsql
AS $$
BEGIN
  RETURN QUERY
    SELECT u.id, u.name, u.email
    FROM users u
    WHERE u.age >= min_age AND u.is_active = TRUE;
END;
$$;

-- Вызов как обычная таблица
SELECT * FROM get_active_users(18);

Категории волатильности

PostgreSQL различает три категории, влияющие на оптимизацию:

  • VOLATILE (по умолчанию) — может возвращать разные результаты при одних входных данных, не кешируется
  • STABLE — в рамках одной транзакции возвращает одинаковый результат для одних параметров
  • IMMUTABLE — результат зависит только от аргументов, можно использовать в индексах
CREATE FUNCTION calculate_tax(amount NUMERIC)
RETURNS NUMERIC
LANGUAGE sql
IMMUTABLE
AS $$
  SELECT amount * 0.2;
$$;

Функции на чистом SQL

Для простых операций без ветвлений можно использовать язык sql:

CREATE OR REPLACE FUNCTION full_name(first_name TEXT, last_name TEXT)
RETURNS TEXT
LANGUAGE sql
IMMUTABLE
AS $$
  SELECT first_name || ' ' || last_name;
$$;

Отличие от процедур (PROCEDURE)

Функция Процедура
Возвращает значение Не возвращает значение
Вызывается в SELECT Вызывается через CALL
Нельзя управлять транзакциями Можно использовать COMMIT/ROLLBACK

Управление функциями

-- Просмотр исходного кода функции
\df+ имя_функции

-- Удаление функции
DROP FUNCTION IF EXISTS get_user_age(INT);

Что хочет услышать интервьюер

Кандидат понимает, что функция хранится в БД и выполняется на стороне сервера

Знает обязательное отличие функции от процедуры — функция должна возвращать значение

Знаком с языками написания функций: минимум PL/pgSQL и SQL

Понимает категории волатильности (VOLATILE/STABLE/IMMUTABLE) и их влияние на производительность

Умеет объяснить, когда использование хранимых функций оправдано (инкапсуляция логики, переиспользование, безопасность)

Пример: Скалярная функция на PL/pgSQL

CREATE OR REPLACE FUNCTION get_discount_price(
  original_price NUMERIC,
  discount_percent INT
)
RETURNS NUMERIC
LANGUAGE plpgsql
STABLE
AS $$
DECLARE
  v_discount NUMERIC;
BEGIN
  -- Проверяем корректность процента скидки
  IF discount_percent < 0 OR discount_percent > 100 THEN
    RAISE EXCEPTION 'Некорректный процент скидки: %', discount_percent;
  END IF;

  v_discount := original_price * discount_percent / 100;
  RETURN original_price - v_discount;
END;
$$;

-- Вызов функции в запросе
SELECT
  product_name,
  price AS original_price,
  get_discount_price(price, 15) AS discounted_price
FROM products
WHERE category = 'electronics';

Пример: Функция возвращающая таблицу (RETURNS TABLE)

CREATE OR REPLACE FUNCTION get_orders_by_status(
  p_status TEXT,
  p_limit INT DEFAULT 10
)
RETURNS TABLE(
  order_id    INT,
  customer    TEXT,
  total_amount NUMERIC,
  created_at  TIMESTAMPTZ
)
LANGUAGE plpgsql
STABLE
AS $$
BEGIN
  RETURN QUERY
    SELECT
      o.id,
      u.full_name,
      o.total_amount,
      o.created_at
    FROM orders o
    JOIN users u ON u.id = o.user_id
    WHERE o.status = p_status
    ORDER BY o.created_at DESC
    LIMIT p_limit;
END;
$$;

-- Вызов: функция ведёт себя как таблица
SELECT * FROM get_orders_by_status('pending', 5);
SELECT customer, total_amount FROM get_orders_by_status('completed') WHERE total_amount > 1000;

Пример: Иммутабельная SQL-функция для использования в индексе

-- Функция нормализации телефона — результат зависит только от аргумента
CREATE OR REPLACE FUNCTION normalize_phone(phone TEXT)
RETURNS TEXT
LANGUAGE sql
IMMUTABLE
AS $$
  -- Оставляем только цифры и заменяем 8 на +7 в начале
  SELECT CASE
    WHEN regexp_replace(phone, '[^0-9]', '', 'g') ~ '^8'
    THEN '+7' || substring(regexp_replace(phone, '[^0-9]', '', 'g') FROM 2)
    ELSE '+' || regexp_replace(phone, '[^0-9]', '', 'g')
  END;
$$;

-- Создание функционального индекса на основе IMMUTABLE-функции
CREATE INDEX idx_users_phone_normalized
  ON users (normalize_phone(phone));

-- Запрос использует индекс
SELECT * FROM users
WHERE normalize_phone(phone) = normalize_phone('+7 (999) 123-45-67');

Типичные ошибки

Путают хранимые функции и процедуры — не знают, что функции обязаны возвращать значение, а процедуры нет

Не указывают категорию волатильности, оставляя VOLATILE там, где подошло бы IMMUTABLE — упускают оптимизации

Забывают про возможность перегрузки функций по типам параметров

Не знают о RETURNS TABLE и пытаются вернуть набор строк через REFCURSOR или другие обходные пути

Считают, что хранимые функции всегда быстрее — не учитывают накладные расходы PL/pgSQL и случаи, когда чистый SQL эффективнее

Лучшие курсы по теме

изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Node.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее