Иногда в цикле всё идёт по плану — до тех пор, пока не появляется причина остановиться раньше. К счастью, PL/pgSQL даёт нам удобные инструменты, чтобы управлять этим процессом.
Прерывание цикла с помощью EXIT
Иногда выполнение цикла нужно завершить раньше, чем он сам достигнет конца. Это может быть вызвано, например, нахождением нужного значения, обнаружением ошибки или завершением итераций по какому-либо условию. В таких ситуациях мы применяем EXIT.
EXIT — это способ "сказать" циклу: "Хватит, ты сделал свою работу, пора остановиться".
Синтаксис EXIT невероятно прост:
EXIT WHEN условие;
Здесь ключевая фраза WHEN указывает, при каком условии выполнение цикла будет завершено. Если условие не указано, EXIT просто мгновенно завершает выполнение текущего цикла.
Пример: завершение цикла, когда найдено заданное значение
Допустим, у нас есть столбец с номерами студентов, и мы хотим найти студента с конкретным идентификатором. Как только студент найден, цикл нужно завершить.
DO $$
DECLARE
student_id INT;
BEGIN
FOR student_id IN 1..100 LOOP
RAISE NOTICE 'Checking student ID: %', student_id;
-- Если нашли нужного студента, завершаем цикл
IF student_id = 42 THEN
RAISE NOTICE 'Student with ID 42 found!';
EXIT;
END IF;
END LOOP;
END $$;
В этом примере цикл перебирает числа от 1 до 100, проверяя каждого "студента". Как только ID 42 найден, цикл выводит сообщение и завершает выполнение.
Пропуск итерации с помощью CONTINUE
Бывают ситуации, когда внутри цикла нужно пропустить определённые итерации, но при этом продолжить выполнение следующей итерации. Это особенно полезно, если вы хотите игнорировать "ненужные" данные или пропустить шаги для специфических условий.
CONTINUE говорит: "Окей, это условие нас не устраивает, просто перейдём к следующей итерации".
CONTINUE работает по тому же принципу, что и EXIT, только вместо завершения цикла он пропускает текущую итерацию:
CONTINUE WHEN условие;
Если условие выполняется, текущая итерация завершится, и выполнение перейдёт к следующей.
Пример: пропуск чётных чисел
В этом примере будем перебирать числа от 1 до 10, но игнорировать чётные числа, выводя только нечётные.
DO $$
DECLARE
num INT;
BEGIN
FOR num IN 1..10 LOOP
-- Пропускаем чётные числа
IF num % 2 = 0 THEN
CONTINUE;
END IF;
RAISE NOTICE 'Odd number: %', num;
END LOOP;
END $$;
Здесь CONTINUE пропускает все итерации, где num % 2 = 0 (т.е. число чётное). Как результат, в лог будут выведены только нечётные числа.
Комбинирование EXIT и CONTINUE
EXIT и CONTINUE можно использовать вместе, чтобы управлять циклом более гибко. Например, вы можете пропускать ненужные итерации с помощью CONTINUE, но завершать весь цикл, если найдено что-то важное.
Вот пример, где мы пропускаем все числа, кратные 3, но завершаем цикл, как только достигаем числа 15.
DO $$
DECLARE
num INT;
BEGIN
FOR num IN 1..20 LOOP
-- Пропускаем числа, кратные 3
IF num % 3 = 0 THEN
CONTINUE;
END IF;
-- Завершаем цикл, если число равно 15
IF num = 15 THEN
RAISE NOTICE 'Stopping at number %', num;
EXIT;
END IF;
RAISE NOTICE 'Current number: %', num;
END LOOP;
END $$;
Здесь цикл работает следующим образом:
- Числа, кратные 3, пропускаются (
CONTINUE). - Если число равно 15, цикл завершает выполнение (
EXIT). - Все остальные числа выводятся.
Продвинутый пример: пропуск некорректных данных и завершение при критической ошибке
Теперь давайте представим более реальную задачу. Мы хотим обработать список студентов, проверяя их данные. Некорректные записи будем пропускать, а в случае "критической ошибки" прекращать обработку.
DO $$
DECLARE
student RECORD;
BEGIN
FOR student IN
SELECT * FROM students
LOOP
-- Пропускаем записи с некорректными данными
IF student.name IS NULL THEN
RAISE NOTICE 'Skipping student with ID %: Missing name', student.id;
CONTINUE;
END IF;
-- Завершаем цикл при критической ошибке
IF student.status = 'ERROR' THEN
RAISE EXCEPTION 'Critical error for student ID %', student.id;
EXIT; -- Эта строка фактически будет избыточной, так как RAISE EXCEPTION завершает выполнение.
END IF;
-- Обработка записи
RAISE NOTICE 'Processing student: %', student.name;
END LOOP;
END $$;
В этом примере CONTINUE помогает пропустить студентов с отсутствующим именем, а EXIT (вместе с RAISE EXCEPTION) завершает цикл при обнаружении серьёзной ошибки.
Практические советы и типичные ошибки
Не забывайте о логике условий. Неправильное использование условных выражений внутри EXIT WHEN или CONTINUE WHEN может привести к неожиданному поведению. Например, цикл может завершиться раньше времени или пропустить важные данные.
Чрезмерное использование CONTINUE. Если ваш код полон проверок с CONTINUE, возможно, стоит пересмотреть логику цикла, чтобы сделать её проще.
Не путайте EXIT и RETURN. EXIT завершает только текущий цикл, в то время как RETURN завершает выполнение функции целиком.
Будьте осторожны с бесконечными циклами. Если вы используете цикл LOOP без чёткого условия завершения, забудьте об EXIT, и ваш цикл может работать бесконечно.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ