LOOP в Oracle PL/SQL. Введение
Есть конструкции, которые кажутся простыми, а потом внезапно оказываются очень мощными.
LOOP в Oracle — как раз из таких. Снаружи это просто цикл, а на практике — основа
для обработки курсоров, коллекций, пакетной логики, генерации тестовых данных, служебных обходов и аккуратной бизнес-автоматизации.
Я люблю LOOP за честность: ты явно управляешь потоком выполнения и понимаешь, где цикл начинается, где заканчивается и при каком условии он должен остановиться.
Синтаксис
LOOP
-- действия
EXIT WHEN condition;
END LOOP;В Oracle чаще всего встречаются три формы:
LOOP с ручным выходом,
WHILE для цикла с условием на входе,
и FOR для диапазонов и курсоров.
Если нужен полный контроль — выбирай базовую форму.
Если границы понятны заранее — часто удобнее FOR.
Где используют
- обработка строк курсора по одной
- обход массивов и коллекций
- генерация тестовых данных
- пакетные обновления и сервисные процедуры
- поиск первого подходящего значения
- сложная логика с вложенными условиями, где одного SELECT уже мало
100 примеров
1. Бесконечный цикл с ручным выходом по счётчику
DECLARE
v_i NUMBER := 0;
BEGIN
LOOP
v_i := v_i + 1;
EXIT WHEN v_i = 5;
DBMS_OUTPUT.PUT_LINE(v_i);
END LOOP;
END;2. Простой проход от 1 до 10 через счётчик
DECLARE
v_i NUMBER := 1;
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE('n=' || v_i);
v_i := v_i + 1;
EXIT WHEN v_i > 10;
END LOOP;
END;3. Сумма чисел до заданного предела
DECLARE
v_i NUMBER := 1;
v_sum NUMBER := 0;
BEGIN
LOOP
v_sum := v_sum + v_i;
v_i := v_i + 1;
EXIT WHEN v_i > 100;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_sum);
END;DECLARE
v_i NUMBER := 1;
BEGIN
LOOP
IF MOD(v_i,7) = 0 THEN
EXIT;
END IF;
DBMS_OUTPUT.PUT_LINE(v_i);
v_i := v_i + 1;
END LOOP;
END;5. Поиск первого чётного числа больше 20
DECLARE
v_n NUMBER := 21;
BEGIN
LOOP
IF MOD(v_n,2) = 0 THEN
DBMS_OUTPUT.PUT_LINE(v_n);
EXIT;
END IF;
v_n := v_n + 1;
END LOOP;
END;6. Генерация строки повторением символа
DECLARE
v_i NUMBER := 1;
v_txt VARCHAR2(100) := '';
BEGIN
LOOP
v_txt := v_txt || '*';
v_i := v_i + 1;
EXIT WHEN v_i > 10;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_txt);
END;7. Накопление произведения значений
DECLARE
v_i NUMBER := 1;
v_prod NUMBER := 1;
BEGIN
LOOP
v_prod := v_prod * v_i;
v_i := v_i + 1;
EXIT WHEN v_i > 5;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_prod);
END;8. Подсчёт количества проходов
DECLARE
v_counter NUMBER := 0;
BEGIN
LOOP
v_counter := v_counter + 1;
EXIT WHEN v_counter = 3;
END LOOP;
DBMS_OUTPUT.PUT_LINE('passes=' || v_counter);
END;9. Формирование списка чисел через конкатенацию
DECLARE
v_i NUMBER := 1;
v_txt VARCHAR2(200) := '';
BEGIN
LOOP
v_txt := v_txt || v_i || ',';
v_i := v_i + 1;
EXIT WHEN v_i > 5;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_txt);
END;10. Проверка условия до записи в лог
BEGIN
LOOP
INSERT INTO audit_events(id,created_at)
SELECT 1,SYSDATE
FROM DUAL;
EXIT;
END LOOP;
END;11. Цикл с накоплением только нечётных значений
DECLARE
v_i NUMBER := 1;
v_sum NUMBER := 0;
BEGIN
LOOP
IF MOD(v_i,2) = 1 THEN
v_sum := v_sum + v_i;
END IF;
v_i := v_i + 1;
EXIT WHEN v_i > 10;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_sum);
END;12. Обратный отсчёт
DECLARE
v_i NUMBER := 5;
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE(v_i);
v_i := v_i - 1;
EXIT WHEN v_i = 0;
END LOOP;
END;13. Запись нескольких тестовых строк через повторение
DECLARE
v_i NUMBER := 1;
BEGIN
LOOP
INSERT INTO temp_session(k,v)
VALUES ('k' || v_i,'v' || v_i);
v_i := v_i + 1;
EXIT WHEN v_i > 3;
END LOOP;
END;14. Генерация случайных значений в таблицу
DECLARE
v_i NUMBER := 1;
BEGIN
LOOP
INSERT INTO ratings(value)
VALUES (TRUNC(DBMS_RANDOM.VALUE(1,6)));
v_i := v_i + 1;
EXIT WHEN v_i > 10;
END LOOP;
END;15. Условный пропуск итерации через CONTINUE
DECLARE
v_i NUMBER := 0;
BEGIN
LOOP
v_i := v_i + 1;
CONTINUE WHEN MOD(v_i,2) = 0;
DBMS_OUTPUT.PUT_LINE(v_i);
EXIT WHEN v_i >= 10;
END LOOP;
END;16. Ранняя остановка при достижении порога суммы
DECLARE
v_i NUMBER := 1;
v_sum NUMBER := 0;
BEGIN
LOOP
v_sum := v_sum + v_i;
EXIT WHEN v_sum >= 30;
v_i := v_i + 1;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_sum);
END;17. Нормализация строки посимвольно
DECLARE
v_txt VARCHAR2(20) := 'Oracle SQL';
v_i NUMBER := 1;
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE(LOWER(SUBSTR(v_txt,v_i,1)));
v_i := v_i + 1;
EXIT WHEN v_i > LENGTH(v_txt);
END LOOP;
END;18. Поиск первого символа пробела
DECLARE
v_txt VARCHAR2(20) := 'a b c';
v_i NUMBER := 1;
BEGIN
LOOP
IF SUBSTR(v_txt,v_i,1) = ' ' THEN
DBMS_OUTPUT.PUT_LINE(v_i);
EXIT;
END IF;
v_i := v_i + 1;
END LOOP;
END;19. Сбор строки в верхнем регистре посимвольно
DECLARE
v_src VARCHAR2(20) := 'oracle';
v_dst VARCHAR2(20) := '';
v_i NUMBER := 1;
BEGIN
LOOP
v_dst := v_dst || UPPER(SUBSTR(v_src,v_i,1));
v_i := v_i + 1;
EXIT WHEN v_i > LENGTH(v_src);
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_dst);
END;20. Мини-генератор дат на 5 дней вперёд
DECLARE
v_i NUMBER := 0;
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE(TO_CHAR(TRUNC(SYSDATE) + v_i,'YYYY-MM-DD'));
v_i := v_i + 1;
EXIT WHEN v_i = 5;
END LOOP;
END;Еще 20 примеров
21. WHILE-цикл для последовательного роста
DECLARE
v_i NUMBER := 1;
BEGIN
WHILE v_i <= 5 LOOP
DBMS_OUTPUT.PUT_LINE(v_i);
v_i := v_i + 1;
END LOOP;
END;22. WHILE с накоплением суммы
DECLARE
v_i NUMBER := 1;
v_sum NUMBER := 0;
BEGIN
WHILE v_i <= 10 LOOP
v_sum := v_sum + v_i;
v_i := v_i + 1;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_sum);
END;23. WHILE для обработки строки по символам
DECLARE
v_txt VARCHAR2(20) := 'dual';
v_i NUMBER := 1;
BEGIN
WHILE v_i <= LENGTH(v_txt) LOOP
DBMS_OUTPUT.PUT_LINE(SUBSTR(v_txt,v_i,1));
v_i := v_i + 1;
END LOOP;
END;24. FOR по диапазону 1..5
BEGIN
FOR i IN 1..5 LOOP
DBMS_OUTPUT.PUT_LINE(i);
END LOOP;
END;25. REVERSE FOR для обратного прохода
BEGIN
FOR i IN REVERSE 1..5 LOOP
DBMS_OUTPUT.PUT_LINE(i);
END LOOP;
END;26. FOR с условием внутри
BEGIN
FOR i IN 1..10 LOOP
IF MOD(i,3)=0 THEN
DBMS_OUTPUT.PUT_LINE(i);
END IF;
END LOOP;
END;27. FOR и вставка 5 тестовых записей
BEGIN
FOR i IN 1..5 LOOP
INSERT INTO temp_cache(id)
VALUES (i);
END LOOP;
END;28. FOR с расчётом квадрата числа
BEGIN
FOR i IN 1..5 LOOP
DBMS_OUTPUT.PUT_LINE(i * i);
END LOOP;
END;29. FOR для построения CSV-строки
DECLARE
v_txt VARCHAR2(100) := '';
BEGIN
FOR i IN 1..5 LOOP
v_txt := v_txt || i || ',';
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_txt);
END;30. FOR для дней месяца
BEGIN
FOR i IN 1..7 LOOP
DBMS_OUTPUT.PUT_LINE(TO_CHAR(TRUNC(SYSDATE,'MM') + i - 1,'DD'));
END LOOP;
END;31. FOR и random для генерации оценок
BEGIN
FOR i IN 1..5 LOOP
INSERT INTO ratings(value)
VALUES (TRUNC(DBMS_RANDOM.VALUE(1,6)));
END LOOP;
END;32. FOR и DECODE для короткой маркировки
BEGIN
FOR i IN 1..3 LOOP
DBMS_OUTPUT.PUT_LINE(DECODE(i,1,'A',2,'B','C'));
END LOOP;
END;33. FOR и CASE для классификации
BEGIN
FOR i IN 1..6 LOOP
DBMS_OUTPUT.PUT_LINE(CASE WHEN i<3 THEN 'low' WHEN i<5 THEN 'mid' ELSE 'high' END);
END LOOP;
END;34. FOR для суммирования чётных чисел
DECLARE
v_sum NUMBER := 0;
BEGIN
FOR i IN 1..10 LOOP
IF MOD(i,2)=0 THEN
v_sum := v_sum + i;
END IF;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_sum);
END;35. FOR для подсчёта длины строки вручную
DECLARE
v_txt VARCHAR2(20) := 'oracle';
v_len NUMBER := 0;
BEGIN
FOR i IN 1..LENGTH(v_txt) LOOP
v_len := v_len + 1;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_len);
END;36. FOR для генерации email-адресов
BEGIN
FOR i IN 1..3 LOOP
DBMS_OUTPUT.PUT_LINE('user' || i || '@example.com');
END LOOP;
END;37. FOR и вставка через sequence
BEGIN
FOR i IN 1..3 LOOP
INSERT INTO users(user_id,email)
VALUES (seq_orders.NEXTVAL,'x' || i || '@mail.com');
END LOOP;
END;38. FOR и TRUNC для генерации дат
BEGIN
FOR i IN 0..6 LOOP
DBMS_OUTPUT.PUT_LINE(TRUNC(SYSDATE) + i);
END LOOP;
END;39. FOR с REVERSE для удаления по убыванию id
BEGIN
FOR i IN REVERSE 1..3 LOOP
DELETE FROM temp_cache WHERE id = i;
END LOOP;
END;40. FOR с EXIT по условию на значении
BEGIN
FOR i IN 1..10 LOOP
EXIT WHEN i = 4;
DBMS_OUTPUT.PUT_LINE(i);
END LOOP;
END;Еще 20 примеров
41. Курсорный проход FOR IN SELECT
BEGIN
FOR r IN (SELECT user_id,email FROM users) LOOP
DBMS_OUTPUT.PUT_LINE(r.email);
END LOOP;
END;42. Курсорный проход с фильтром WHERE
BEGIN
FOR r IN (SELECT order_id,user_id FROM orders WHERE order_date >= TRUNC(SYSDATE)) LOOP
DBMS_OUTPUT.PUT_LINE(r.order_id);
END LOOP;
END;43. Курсор по представлению с JOIN
BEGIN
FOR r IN (
SELECT u.email,o.order_id
FROM users u
JOIN orders o ON o.user_id = u.user_id
) LOOP
DBMS_OUTPUT.PUT_LINE(r.email || ':' || r.order_id);
END LOOP;
END;44. Курсор с ORDER BY для упорядоченного обхода
BEGIN
FOR r IN (
SELECT order_id,order_date
FROM orders
ORDER BY order_date DESC
) LOOP
DBMS_OUTPUT.PUT_LINE(r.order_id);
END LOOP;
END;45. Курсор и накопление общей суммы
DECLARE
v_total NUMBER := 0;
BEGIN
FOR r IN (SELECT amount FROM payments) LOOP
v_total := v_total + r.amount;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_total);
END;46. Курсор и выбор максимального значения
DECLARE
v_max NUMBER := 0;
BEGIN
FOR r IN (SELECT amount FROM payments) LOOP
IF r.amount > v_max THEN
v_max := r.amount;
END IF;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_max);
END;47. Курсор и пропуск пустых email через CONTINUE
BEGIN
FOR r IN (SELECT email FROM users) LOOP
CONTINUE WHEN r.email IS NULL;
DBMS_OUTPUT.PUT_LINE(r.email);
END LOOP;
END;48. Курсор и раннее завершение после первой записи
BEGIN
FOR r IN (SELECT user_id FROM users ORDER BY user_id) LOOP
DBMS_OUTPUT.PUT_LINE(r.user_id);
EXIT;
END LOOP;
END;49. Курсор по константному набору через UNION ALL
BEGIN
FOR r IN (
SELECT 'A' code FROM DUAL
UNION ALL
SELECT 'B' FROM DUAL
) LOOP
DBMS_OUTPUT.PUT_LINE(r.code);
END LOOP;
END;50. Курсор и UPDATE по найденным строкам
BEGIN
FOR r IN (SELECT task_id FROM tasks WHERE status='NEW') LOOP
UPDATE tasks
SET status='DONE'
WHERE task_id = r.task_id;
END LOOP;
END;51. Курсор и DELETE по условию
BEGIN
FOR r IN (SELECT id FROM temp_session WHERE k IS NULL) LOOP
DELETE FROM temp_session WHERE id = r.id;
END LOOP;
END;52. Курсор и INSERT в аудит
BEGIN
FOR r IN (SELECT order_id FROM orders) LOOP
INSERT INTO audit_events(id,created_at)
VALUES (r.order_id,SYSDATE);
END LOOP;
END;53. Явный cursor OPEN FETCH EXIT WHEN NOTFOUND
DECLARE
CURSOR c IS SELECT user_id,email FROM users;
v_id users.user_id%TYPE;
v_email users.email%TYPE;
BEGIN
OPEN c;
LOOP
FETCH c INTO v_id,v_email;
EXIT WHEN c%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_email);
END LOOP;
CLOSE c;
END;54. Явный cursor с подсчётом строк
DECLARE
CURSOR c IS SELECT order_id FROM orders;
v_id orders.order_id%TYPE;
v_cnt NUMBER := 0;
BEGIN
OPEN c;
LOOP
FETCH c INTO v_id;
EXIT WHEN c%NOTFOUND;
v_cnt := v_cnt + 1;
END LOOP;
CLOSE c;
DBMS_OUTPUT.PUT_LINE(v_cnt);
END;55. Явный cursor и поиск первого совпадения
DECLARE
CURSOR c IS SELECT email FROM users ORDER BY user_id;
v_email users.email%TYPE;
BEGIN
OPEN c;
LOOP
FETCH c INTO v_email;
EXIT WHEN c%NOTFOUND;
IF v_email LIKE '%@example.com' THEN
DBMS_OUTPUT.PUT_LINE(v_email);
EXIT;
END IF;
END LOOP;
CLOSE c;
END;56. Явный cursor с обработкой пустой выборки
DECLARE
CURSOR c IS SELECT user_id FROM users WHERE 1=0;
v_id users.user_id%TYPE;
BEGIN
OPEN c;
LOOP
FETCH c INTO v_id;
EXIT WHEN c%NOTFOUND;
END LOOP;
IF c%ROWCOUNT = 0 THEN
DBMS_OUTPUT.PUT_LINE('empty');
END IF;
CLOSE c;
END;57. Явный cursor с CASE внутри обработки
DECLARE
CURSOR c IS SELECT amount FROM payments;
v_amount payments.amount%TYPE;
BEGIN
OPEN c;
LOOP
FETCH c INTO v_amount;
EXIT WHEN c%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(CASE WHEN v_amount > 1000 THEN 'big' ELSE 'small' END);
END LOOP;
CLOSE c;
END;58. Явный cursor + BULK-like батчи вручную
DECLARE
CURSOR c IS SELECT order_id FROM orders;
v_id orders.order_id%TYPE;
v_cnt NUMBER := 0;
BEGIN
OPEN c;
LOOP
FETCH c INTO v_id;
EXIT WHEN c%NOTFOUND;
v_cnt := v_cnt + 1;
IF MOD(v_cnt,100) = 0 THEN
COMMIT;
END IF;
END LOOP;
CLOSE c;
END;59. Вложенный проход по пользователям и заказам
BEGIN
FOR u IN (SELECT user_id FROM users) LOOP
FOR o IN (SELECT order_id FROM orders WHERE user_id = u.user_id) LOOP
DBMS_OUTPUT.PUT_LINE(u.user_id || ':' || o.order_id);
END LOOP;
END LOOP;
END;60. Метки label для внешнего EXIT
BEGIN
<<outer_loop>>
FOR i IN 1..3 LOOP
FOR j IN 1..3 LOOP
EXIT outer_loop WHEN i = 2 AND j = 2;
DBMS_OUTPUT.PUT_LINE(i || ',' || j);
END LOOP;
END LOOP;
END;Еще 20 примеров
61. Обход associative array по индексам
DECLARE
TYPE t_map IS TABLE OF VARCHAR2(20) INDEX BY PLS_INTEGER;
v_map t_map;
v_i PLS_INTEGER;
BEGIN
v_map(1) := 'A';
v_map(2) := 'B';
v_i := v_map.FIRST;
LOOP
EXIT WHEN v_i IS NULL;
DBMS_OUTPUT.PUT_LINE(v_map(v_i));
v_i := v_map.NEXT(v_i);
END LOOP;
END;62. Обход nested table через FIRST..LAST
DECLARE
TYPE t_list IS TABLE OF NUMBER;
v_list t_list := t_list(10,20,30);
BEGIN
FOR i IN v_list.FIRST..v_list.LAST LOOP
DBMS_OUTPUT.PUT_LINE(v_list(i));
END LOOP;
END;63. Обход varray и сумма элементов
DECLARE
TYPE t_arr IS VARRAY(5) OF NUMBER;
v_arr t_arr := t_arr(1,2,3,4,5);
v_sum NUMBER := 0;
BEGIN
FOR i IN 1..v_arr.COUNT LOOP
v_sum := v_sum + v_arr(i);
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_sum);
END;64. Обход sparse-массива через NEXT
DECLARE
TYPE t_map IS TABLE OF VARCHAR2(20) INDEX BY PLS_INTEGER;
v_map t_map;
v_i PLS_INTEGER;
BEGIN
v_map(10) := 'x';
v_map(20) := 'y';
v_i := v_map.FIRST;
LOOP
EXIT WHEN v_i IS NULL;
DBMS_OUTPUT.PUT_LINE(v_i || '=' || v_map(v_i));
v_i := v_map.NEXT(v_i);
END LOOP;
END;65. Удаление чётных элементов из коллекции
DECLARE
TYPE t_list IS TABLE OF NUMBER;
v_list t_list := t_list(1,2,3,4,5);
BEGIN
FOR i IN REVERSE 1..v_list.COUNT LOOP
IF MOD(v_list(i),2)=0 THEN
v_list.DELETE(i);
END IF;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_list.COUNT);
END;66. Преобразование коллекции в CSV
DECLARE
TYPE t_list IS TABLE OF VARCHAR2(10);
v_list t_list := t_list('A','B','C');
v_txt VARCHAR2(50) := '';
BEGIN
FOR i IN 1..v_list.COUNT LOOP
v_txt := v_txt || v_list(i) || ',';
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_txt);
END;67. BULK COLLECT + обход результата
DECLARE
TYPE t_ids IS TABLE OF users.user_id%TYPE;
v_ids t_ids;
BEGIN
SELECT user_id BULK COLLECT INTO v_ids FROM users;
FOR i IN 1..v_ids.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(v_ids(i));
END LOOP;
END;68. BULK COLLECT LIMIT в явном курсоре
DECLARE
CURSOR c IS SELECT user_id FROM users;
TYPE t_ids IS TABLE OF users.user_id%TYPE;
v_ids t_ids;
BEGIN
OPEN c;
LOOP
FETCH c BULK COLLECT INTO v_ids LIMIT 100;
EXIT WHEN v_ids.COUNT = 0;
FOR i IN 1..v_ids.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(v_ids(i));
END LOOP;
END LOOP;
CLOSE c;
END;69. FORALL после подготовки коллекции
DECLARE
TYPE t_vals IS TABLE OF NUMBER;
v_vals t_vals := t_vals(101,102,103);
BEGIN
FORALL i IN 1..v_vals.COUNT
INSERT INTO temp_cache(id) VALUES (v_vals(i));
END;70. Подготовка коллекции в цикле перед FORALL
DECLARE
TYPE t_vals IS TABLE OF NUMBER;
v_vals t_vals := t_vals();
BEGIN
FOR i IN 1..5 LOOP
v_vals.EXTEND;
v_vals(v_vals.LAST) := i * 10;
END LOOP;
FORALL j IN 1..v_vals.COUNT
INSERT INTO temp_cache(id) VALUES (v_vals(j));
END;71. WHILE и ассоциативный массив с PREV/NEXT
DECLARE
TYPE t_map IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
v_map t_map;
v_i PLS_INTEGER;
BEGIN
v_map(1) := 10;
v_map(3) := 30;
v_i := v_map.LAST;
WHILE v_i IS NOT NULL LOOP
DBMS_OUTPUT.PUT_LINE(v_map(v_i));
v_i := v_map.PREV(v_i);
END LOOP;
END;72. Обход массива строк и поиск первого совпадения
DECLARE
TYPE t_arr IS TABLE OF VARCHAR2(10);
v_arr t_arr := t_arr('red','blue','green');
BEGIN
FOR i IN 1..v_arr.COUNT LOOP
IF v_arr(i) = 'blue' THEN
DBMS_OUTPUT.PUT_LINE(i);
EXIT;
END IF;
END LOOP;
END;73. Удвоение значений коллекции на месте
DECLARE
TYPE t_arr IS TABLE OF NUMBER;
v_arr t_arr := t_arr(1,2,3);
BEGIN
FOR i IN 1..v_arr.COUNT LOOP
v_arr(i) := v_arr(i) * 2;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_arr(3));
END;74. Нормализация email-ов в массиве
DECLARE
TYPE t_arr IS TABLE OF VARCHAR2(50);
v_arr t_arr := t_arr('A@MAIL.COM','B@MAIL.COM');
BEGIN
FOR i IN 1..v_arr.COUNT LOOP
v_arr(i) := LOWER(v_arr(i));
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_arr(1));
END;75. Фильтрация коллекции по порогу
DECLARE
TYPE t_arr IS TABLE OF NUMBER;
v_arr t_arr := t_arr(5,15,25);
BEGIN
FOR i IN 1..v_arr.COUNT LOOP
CONTINUE WHEN v_arr(i) < 10;
DBMS_OUTPUT.PUT_LINE(v_arr(i));
END LOOP;
END;76. Генерация вложенной коллекции чисел
DECLARE
TYPE t_arr IS TABLE OF NUMBER;
v_arr t_arr := t_arr();
BEGIN
FOR i IN 1..4 LOOP
v_arr.EXTEND;
v_arr(i) := i * i;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_arr(4));
END;77. Подготовка тестовых кодов в коллекции
DECLARE
TYPE t_arr IS TABLE OF VARCHAR2(10);
v_arr t_arr := t_arr();
BEGIN
FOR i IN 1..3 LOOP
v_arr.EXTEND;
v_arr(i) := 'C' || i;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_arr(2));
END;78. Подсчёт общей длины элементов массива
DECLARE
TYPE t_arr IS TABLE OF VARCHAR2(20);
v_arr t_arr := t_arr('aa','bbb','cccc');
v_sum NUMBER := 0;
BEGIN
FOR i IN 1..v_arr.COUNT LOOP
v_sum := v_sum + LENGTH(v_arr(i));
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_sum);
END;79. Двумерная логика через вложенные циклы
BEGIN
FOR i IN 1..3 LOOP
FOR j IN 1..3 LOOP
DBMS_OUTPUT.PUT_LINE(i || '*' || j || '=' || i*j);
END LOOP;
END LOOP;
END;80. Матрица дат и часов
BEGIN
FOR d IN 0..1 LOOP
FOR h IN 0..2 LOOP
DBMS_OUTPUT.PUT_LINE(TO_CHAR(TRUNC(SYSDATE)+d + h/24,'YYYY-MM-DD HH24:MI'));
END LOOP;
END LOOP;
END;Еще 20 примеров
81. Обработка ошибок внутри цикла и продолжение
BEGIN
FOR i IN 1..3 LOOP
BEGIN
DBMS_OUTPUT.PUT_LINE(10 / (2 - i));
EXCEPTION
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('zero');
END;
END LOOP;
END;82. Retry-логика с ограничением попыток
DECLARE
v_try NUMBER := 0;
BEGIN
LOOP
v_try := v_try + 1;
EXIT WHEN v_try = 3;
END LOOP;
DBMS_OUTPUT.PUT_LINE('tries=' || v_try);
END;83. Ожидание события с верхней границей итераций
DECLARE
v_i NUMBER := 0;
v_ready NUMBER := 0;
BEGIN
LOOP
v_i := v_i + 1;
IF v_i = 4 THEN
v_ready := 1;
END IF;
EXIT WHEN v_ready = 1 OR v_i = 10;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_ready);
END;84. Поиск первого NULL в массиве
DECLARE
TYPE t_arr IS TABLE OF VARCHAR2(10);
v_arr t_arr := t_arr('A',NULL,'C');
BEGIN
FOR i IN 1..v_arr.COUNT LOOP
IF v_arr(i) IS NULL THEN
DBMS_OUTPUT.PUT_LINE(i);
EXIT;
END IF;
END LOOP;
END;85. Обход диапазона дат по месяцам
DECLARE
v_dt DATE := DATE '2026-01-01';
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE(TO_CHAR(v_dt,'YYYY-MM'));
v_dt := ADD_MONTHS(v_dt,1);
EXIT WHEN v_dt > DATE '2026-06-01';
END LOOP;
END;86. Генерация рабочих дней без выходных
DECLARE
v_dt DATE := TRUNC(SYSDATE);
v_cnt NUMBER := 0;
BEGIN
LOOP
IF TO_CHAR(v_dt,'DY','NLS_DATE_LANGUAGE=ENGLISH') NOT IN ('SAT','SUN') THEN
DBMS_OUTPUT.PUT_LINE(TO_CHAR(v_dt,'YYYY-MM-DD'));
v_cnt := v_cnt + 1;
END IF;
v_dt := v_dt + 1;
EXIT WHEN v_cnt = 5;
END LOOP;
END;87. Пошаговая пакетная очистка с COMMIT
DECLARE
v_rows NUMBER := 1;
BEGIN
LOOP
DELETE FROM temp_session WHERE ROWNUM <= 100;
v_rows := SQL%ROWCOUNT;
COMMIT;
EXIT WHEN v_rows = 0;
END LOOP;
END;88. Пакетное обновление кусками по 100 строк
DECLARE
v_rows NUMBER := 1;
BEGIN
LOOP
UPDATE tasks
SET status = 'DONE'
WHERE task_id IN (
SELECT task_id
FROM tasks
WHERE status = 'NEW'
AND ROWNUM <= 100
);
v_rows := SQL%ROWCOUNT;
COMMIT;
EXIT WHEN v_rows = 0;
END LOOP;
END;89. Динамический SQL в цикле по именам таблиц
DECLARE
TYPE t_arr IS TABLE OF VARCHAR2(30);
v_arr t_arr := t_arr('USERS','ORDERS');
BEGIN
FOR i IN 1..v_arr.COUNT LOOP
EXECUTE IMMEDIATE 'TRUNCATE TABLE ' || v_arr(i);
END LOOP;
END;90. Динамический SQL для удаления временных объектов
DECLARE
TYPE t_arr IS TABLE OF VARCHAR2(30);
v_arr t_arr := t_arr('TMP_A','TMP_B');
BEGIN
FOR i IN 1..v_arr.COUNT LOOP
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE ' || v_arr(i);
EXCEPTION
WHEN OTHERS THEN NULL;
END;
END LOOP;
END;91. Обход списка ID и вызов процедуры
DECLARE
TYPE t_ids IS TABLE OF NUMBER;
v_ids t_ids := t_ids(10,20,30);
BEGIN
FOR i IN 1..v_ids.COUNT LOOP
write_event(v_ids(i));
END LOOP;
END;92. Многошаговая валидация с EXIT WHEN
DECLARE
v_step NUMBER := 1;
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE('step=' || v_step);
EXIT WHEN v_step = 4;
v_step := v_step + 1;
END LOOP;
END;93. Цикл с накоплением до первого превышения лимита
DECLARE
v_i NUMBER := 1;
v_sum NUMBER := 0;
BEGIN
LOOP
v_sum := v_sum + i;
EXIT WHEN v_sum > 50;
v_i := v_i + 1;
END LOOP;
END;94. Печать шахматного узора символами
DECLARE
v_line VARCHAR2(20);
BEGIN
FOR i IN 1..4 LOOP
v_line := '';
FOR j IN 1..4 LOOP
v_line := v_line || CASE WHEN MOD(i+j,2)=0 THEN '#' ELSE '.' END;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_line);
END LOOP;
END;95. Итеративный поиск степени двойки
DECLARE
v_val NUMBER := 1;
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE(v_val);
v_val := v_val * 2;
EXIT WHEN v_val > 64;
END LOOP;
END;96. Поиск первого дня месяца, который выпал на понедельник
DECLARE
v_dt DATE := DATE '2026-01-01';
BEGIN
LOOP
IF TO_CHAR(v_dt,'DY','NLS_DATE_LANGUAGE=ENGLISH') = 'MON' THEN
DBMS_OUTPUT.PUT_LINE(TO_CHAR(v_dt,'YYYY-MM-DD'));
EXIT;
END IF;
v_dt := ADD_MONTHS(v_dt,1);
END LOOP;
END;97. Подготовка отчётных строк за 12 месяцев
DECLARE
v_dt DATE := TRUNC(SYSDATE,'YYYY');
BEGIN
FOR i IN 1..12 LOOP
INSERT INTO temp_session(k,v)
VALUES ('month',TO_CHAR(ADD_MONTHS(v_dt,i - 1),'YYYY-MM'));
END LOOP;
END;98. Цикл по JSON-ключам из заранее известного набора
DECLARE
TYPE t_arr IS TABLE OF VARCHAR2(20);
v_keys t_arr := t_arr('id','name','status');
BEGIN
FOR i IN 1..v_keys.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(v_keys(i));
END LOOP;
END;99. Имитация polling-проверки до успеха
DECLARE
v_try NUMBER := 0;
v_ok NUMBER := 0;
BEGIN
LOOP
v_try := v_try + 1;
IF v_try = 5 THEN
v_ok := 1;
END IF;
EXIT WHEN v_ok = 1 OR v_try = 10;
END LOOP;
DBMS_OUTPUT.PUT_LINE(v_ok);
END;100. Финальный универсальный шаблон с EXIT WHEN и журналированием
DECLARE
v_i NUMBER := 0;
BEGIN
LOOP
v_i := v_i + 1;
INSERT INTO audit_events(id,created_at)
VALUES (v_i,SYSDATE);
EXIT WHEN v_i = 3;
END LOOP;
END;Заключение
LOOP в Oracle — это не просто цикл ради цикла. Это способ аккуратно управлять логикой,
когда нужна последовательность действий, обход результата, подготовка данных или понятный контроль над потоком.
Люблю его за то, что он заставляет писать осознанно: ты видишь каждую итерацию, каждое условие выхода и каждый побочный эффект.
Если SQL — это язык данных, то PL/SQL с циклами — уже настоящая инженерия.
Официальная документация Oracle:
https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/loop-statements.html
🔜 Следующая статья:
KEY в Oracle SQL — как создавать связи между таблицами