Циклы позволяют выполнять блок кода несколько раз подряд, пока выполняется определённое условие или пока выполняется итерация по какому-либо набору данных. Это крайне полезно для автоматизации задач, обработки больших объемов данных или выполнения повторяющихся операций.
Представьте, что вы должны вручную раздать конфеты всем детям в группе. Это будет хаос! Вместо этого вы можете пройтись по списку детей и каждому по очереди дать конфету. В этом сценарии "проход по списку" — это цикл.
В PostgreSQL поддерживаются несколько видов циклов:
LOOP— универсальный цикл, который выполняется до тех пор, пока явно не будет остановлен.FOR— цикл с заданным количеством итераций по диапазону чисел или результату запроса.WHILE— цикл, который выполняется до тех пор, пока условие истинно.
Давайте разберём каждый вид по отдельности.
Бесконечный цикл:LOOP
LOOP — это базовая форма цикла в PL/pgSQL, которая повторяет выполнение блока кода. Этот цикл работает бесконечно, и его выполнение нужно завершать вручную с помощью команды EXIT.
Синтаксис:
LOOP
-- Ваш код здесь
END LOOP;
Приведём пример. Посчитаем сумму чисел от 1 до 10 с помощью LOOP:
DO $$
DECLARE
counter INT := 1;
sum INT := 0;
BEGIN
LOOP
sum := sum + counter; -- добавляем текущее значение счётчика
counter := counter + 1; -- увеличиваем счётчик
-- Завершаем цикл, если счётчик больше 10
IF counter > 10 THEN
EXIT;
END IF;
END LOOP;
RAISE NOTICE 'Сумма чисел от 1 до 10: %', sum;
END $$;
- Переменная
counterувеличивается на каждой итерации. - Условие
IF counter > 10 THEN EXIT;завершает цикл, когда счётчик превышает 10. - В конце выводится сумма чисел.
Цикл по диапазону или набору данных: FOR
Второй тип цикла, FOR, используется для итерации по:
- Диапазону чисел.
- Результату SQL-запроса.
Цикл по диапазону чисел
Синтаксис:
FOR variable IN [REVERSE] start..end LOOP
-- Ваш код здесь
END LOOP;
Выведем числа от 1 до 5:
DO $$
BEGIN
FOR i IN 1..5 LOOP
RAISE NOTICE 'Текущее значение: %', i;
END LOOP;
END $$;
Пример с обратным порядком:
DO $$
BEGIN
FOR i IN REVERSE 5..1 LOOP
RAISE NOTICE 'Обратный порядок: %', i;
END LOOP;
END $$;
Цикл по результату SQL-запроса
Этот вариант полезен для обработки строк таблицы.
Синтаксис:
FOR variable IN QUERY (SELECT ...) LOOP
-- Ваш код здесь
END LOOP;
Приведём пример итерации по строкам таблицы students:
DO $$
DECLARE
student_name TEXT;
BEGIN
FOR student_name IN QUERY (SELECT name FROM students) LOOP
RAISE NOTICE 'Привет, %!', student_name;
END LOOP;
END $$;
Условный цикл:WHILE
WHILE выполняется до тех пор, пока заданное условие истинно.
Синтаксис:
WHILE условие LOOP
-- Ваш код здесь
END LOOP;
Давайте посчитаем сумму чисел от 1 до 10 с помощью WHILE:
DO $$
DECLARE
counter INT := 1;
sum INT := 0;
BEGIN
WHILE counter <= 10 LOOP
sum := sum + counter;
counter := counter + 1;
END LOOP;
RAISE NOTICE 'Сумма чисел от 1 до 10: %', sum;
END $$;
Примеры из реальной жизни
Теперь, когда мы разобрались с основами циклов, давайте взглянем на примеры их применения.
Пример 1: генерация таблицы умножения
DO $$
DECLARE
i INT;
j INT;
BEGIN
FOR i IN 1..5 LOOP
FOR j IN 1..5 LOOP
RAISE NOTICE '% x % = %', i, j, i * j;
END LOOP;
END LOOP;
END $$;
Пример 2: итерация по результатам запроса. Предположим, у нас есть таблица products с полями id и price. Обновим все цены, увеличив их на 10%.
DO $$
DECLARE
prod RECORD;
BEGIN
FOR prod IN QUERY (SELECT id, price FROM products) LOOP
UPDATE products
SET price = prod.price * 1.1
WHERE id = prod.id;
END LOOP;
END $$;
Типичные ошибки и как их избежать
Зацикливание в LOOP или WHILE. Если забыть добавить условие выхода (EXIT или корректное условие в WHILE), цикл никогда не завершится. Примерно то же, что водить машину без тормозов.
Ошибка при итерации по результату запроса. Если структура результирующих данных не соответствует ожидаемому, можно столкнуться с ошибкой в цикле FOR IN QUERY.
Неоптимальные запросы внутри циклов. Например, выполнение UPDATE в каждой итерации может сильно замедлить выполнение. В таких случаях лучше использовать один SQL-запрос вместо цикла.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ