Давайте глубже изучим PL/pgSQL и начнём им пользоваться более активно.
Блок кода
Блок кода в PL/pgSQL — это основной строительный элемент языка. Можно сказать, что это каркас, на котором держатся наши функции, процедуры и прочая магия. Блок обеспечивает выполнение логики, обработку данных, управление ошибками, и всё это в одном "контейнере".
PL/pgSQL блоки структурированы и включают три основные части:
DECLARE: объявление переменных (опционально).BEGIN ... END: основной блок выполнения, где происходит выполнение логики.EXCEPTION: обработка ошибок (опционально).
Для любителей аналогий: представьте себе рецепт блюда. Хотя текст рецепта может начинаться со списка ингредиентов, основная магия случается в самом процессе готовки. В терминах PL/pgSQL:
DECLARE— это список ингредиентов (переменные).BEGIN ... END— это то место, где смешивают, жарят и варят.EXCEPTION— это план действий, если что-то пригорит.
Синтаксис блока PL/pgSQL
Сначала посмотрим на общую структуру блока, как на "скелет". Позже добавим мясо (или вегетарианский сыр, если предпочитаете) — конкретную логику.
DO $$
DECLARE
-- Здесь объявляются переменные
student_count INT;
BEGIN
-- Здесь выполняется логика
SELECT COUNT(*) INTO student_count FROM students;
RAISE NOTICE 'Total number of students: %', student_count;
EXCEPTION
-- Здесь обрабатываются ошибки
WHEN OTHERS THEN
RAISE NOTICE 'An error occurred.';
END;
$$;
Давайте разберем это пошагово.
DECLARE— тут мы объявляем наши переменные. Здорово то, что PL/pgSQL поддерживает почти все типы данных, доступные в PostgreSQL — от скромныхINTEGERдо экзотических JSONB. Чтобы объявить переменную, нужно указать её имя, тип данных и, если нужно, начальное значение.
Пример:
DECLARE
student_name TEXT; -- Переменная для имени студента
course_count INT := 0; -- Задаем начальное значение равным 0
is_graduated BOOLEAN; -- Логическая переменная
Обратите внимание, что переменные могут быть с инициализацией (как course_count) или без.
BEGIN ... END— основной блок выполнения.
Эта часть блока отвечает за выполнение основной логики. Здесь мы можем:
- Выполнять SQL-запросы (
SELECT,INSERT, и т.д.). - Манипулировать данными.
- Использовать управляющие конструкции (
IF,LOOP, и т.д.). - Выводить отладочные сообщения с помощью
RAISE.
Пример:
BEGIN
SELECT COUNT(*) INTO student_count FROM students;
IF student_count > 0 THEN
RAISE NOTICE 'We have students!';
ELSE
RAISE NOTICE 'No students found.';
END IF;
END;
EXCEPTION— обработка ошибок (опционально).
Если в процессе выполнения блока произойдет ошибка, секция EXCEPTION позволяет нам перехватить её и сделать что-то полезное — например, вывести сообщение или выполнить альтернативный код.
Пример:
BEGIN
SELECT COUNT(*) INTO student_count FROM non_existing_table; -- Ошибка!
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'Oops, something went wrong!';
END;
Реальный пример: подсчёт студентов
Теперь соберём все части вместе в пример, который может пригодиться в реальной жизни. Напишем блок PL/pgSQL, который считает количество студентов в таблице students и выводит сообщение.
DO $$
DECLARE
total_students INT; -- Переменная для хранения количества студентов
BEGIN
-- Подсчитываем количество студентов
SELECT COUNT(*) INTO total_students FROM students;
-- Выводим сообщение с результатом
RAISE NOTICE 'Number of students: %', total_students;
EXCEPTION
-- Обрабатываем возможные ошибки, например, если таблица не существует
WHEN OTHERS THEN
RAISE NOTICE 'An error occurred while counting students.';
END;
$$;
Вызов этого блока выведет сообщение в консоль. Например: Number of students: 42.
Особенности использования переменных
Давайте разберём несколько важных моментов:
Присваивание значений переменным. Чтобы записать данные в переменную, можно использовать оператор SELECT INTO:
SELECT COUNT(*) INTO total_students FROM students;
Инициализация переменных. Если вы не присвоили значение переменной при объявлении, ее значение по умолчанию будет NULL.
Например:
DECLARE
my_var INT; -- Значение NULL
Переменные типа RECORD. Это универсальный тип переменной, в которую можно помещать строки из таблицы. Пример:
DECLARE
student RECORD;
BEGIN
SELECT * INTO student FROM students WHERE id = 1;
RAISE NOTICE 'Student Name: %, Age: %', student.name, student.age;
END;
Пример: подсчёт курсов для студента
Теперь решим практическую задачу: подсчитать, сколько курсов записано за студентом, и вывести результат.
DO $$
DECLARE
student_id INT := 1; -- ID студента
course_count INT; -- Переменная для количества курсов
BEGIN
-- Подсчитываем количество курсов
SELECT COUNT(*) INTO course_count
FROM enrollments
WHERE student_id = student_id;
-- Выводим сообщение
RAISE NOTICE 'Student ID % is enrolled in % courses.', student_id, course_count;
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'An error occurred while processing student ID %', student_id;
END;
$$;
Этот блок гибко работает: можно менять student_id, чтобы проверить, сколько курсов записано за разными студентами.
Ошибки и их предотвращение
Если PL/pgSQL внутри вас уже бьётся как маленький хот-дог в микроволновке, это нормально. На первых порах часто можно столкнуться с "типичными" ошибками. Вот несколько примеров:
Отсутствие объявления переменной. Если вы забыли объявить переменную через DECLARE, PL/pgSQL выдаст ошибку, что переменной "не существует".
Попытка использовать NULL как значение. Если переменная была объявлена, но ей не присвоено значение, результатом будет NULL. Это может вызывать неожиданные поведения. Например:
IF my_var = NULL THEN -- НЕ сработает!
Используйте IS NULL:
IF my_var IS NULL THEN
Неправильное использование секции EXCEPTION. Иногда программисты перехватывают все ошибки (WHEN OTHERS), но не пишут, что делать. Это может скрыть реальную проблему. Лучше записывать сообщение об ошибке:
RAISE NOTICE 'Error: %', SQLERRM;
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ