JavaRush /Курсы /SQL SELF /Вложенные циклы и рекурсия

Вложенные циклы и рекурсия

SQL SELF
51 уровень , 3 лекция
Открыта

Вложенные циклы — это циклы, которые работают внутри других циклов. Представьте себе секцию вопросов на собеседовании, когда один вопрос приводит к новым уточняющим вопросам. Логика вложенных циклов такова: внешний цикл выполняет итерацию, а внутренний цикл совершает проходы для каждой итерации внешнего цикла.

Пример: таблица умножения

Напишем функцию, которая создаёт таблицу умножения для чисел от 1 до 5. Это классический пример, где используются вложенные циклы:

CREATE OR REPLACE FUNCTION generate_multiplication_table()
RETURNS VOID AS $$
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;
$$ LANGUAGE plpgsql;

-- Вызов функции:
SELECT generate_multiplication_table();

Логика работы:

  1. Внешний цикл перехватывает значения i от 1 до 5.
  2. Для каждого значения i, внутренний цикл берёт значения j от 1 до 5.
  3. На каждом шаге комбинируются значения i и j, чтобы вычислить результат умножения.
  4. Вывод выглядит как мини-таблица:
   1 x 1 = 1
   1 x 2 = 2
   ...
   5 x 5 = 25

Практика: поиск пересечений в двух таблицах

Теперь давайте создадим более "жизненный" пример. Представьте, что у нас есть две таблицы:

  • students (студенты, их имена),
  • courses (курсы, в которых они записаны).

Мы хотим найти студентов, которые записаны на более чем один курс. Используем вложенные циклы:

CREATE OR REPLACE FUNCTION find_students_with_multiple_courses()
RETURNS TABLE(student_name TEXT, course_name TEXT) AS $$
BEGIN
  FOR student IN SELECT DISTINCT student_name FROM students LOOP
    FOR course IN SELECT DISTINCT course_name FROM courses WHERE student_id = student.student_id LOOP
      RETURN QUERY SELECT student.student_name, course.course_name;
    END LOOP;
  END LOOP;
END;
$$ LANGUAGE plpgsql;

-- Вызов функции:
SELECT * FROM find_students_with_multiple_courses();

Рекурсия

Рекурсия — это когда функция вызывает саму себя. Это как если бы вы попросили друга объяснить SQL, а он предложил прочитать документацию, которая ссылается на этот же урок… Не стоит путать рекурсию с бесконечным циклом. У рекурсии всегда есть "условие остановки" (точка, в которой функция прекращает вызывать саму себя).

Пример: вычисление факториала числа

Факториал числа n — это произведение всех чисел от 1 до n. Например, факториал 5 (обозначается 5!) равен 5 * 4 * 3 * 2 * 1 = 120. Вот как это можно реализовать с помощью рекурсии:

CREATE OR REPLACE FUNCTION calculate_factorial(n INTEGER)
RETURNS INTEGER AS $$
BEGIN
  -- Условие остановки: факториал 0 или 1 равен 1
  IF n = 0 OR n = 1 THEN
    RETURN 1;
  END IF;

  -- Рекурсивный вызов функции
  RETURN n * calculate_factorial(n - 1);
END;
$$ LANGUAGE plpgsql;

-- Вызов функции:
SELECT calculate_factorial(5); -- Результат: 120

Логика работы:

  1. Если n равно 0 или 1, возвращаем 1.
  2. Если n > 1, функция вызывает саму себя с аргументом n - 1 и умножает это значение на n.
  3. Таким образом, вызовы "накапливаются", а затем сворачиваются в одном направлении.

Практический пример: числа Фибоначчи

Числа Фибоначчи — это последовательность чисел, где каждое число является суммой двух предыдущих. Последовательность начинается так: 0, 1, 1, 2, 3, 5, 8....

Давайте напишем функцию для вычисления n-го числа в последовательности:

CREATE OR REPLACE FUNCTION fibonacci(n INTEGER)
RETURNS INTEGER AS $$
BEGIN
  -- Условие остановки: первые два числа известны
  IF n = 0 THEN
    RETURN 0;
  ELSIF n = 1 THEN
    RETURN 1;
  END IF;

  -- Рекурсивный вызов функции
  RETURN fibonacci(n - 1) + fibonacci(n - 2);
END;
$$ LANGUAGE plpgsql;

-- Вызов функции:
SELECT fibonacci(6); -- Результат: 8

Когда использовать вложенные циклы и рекурсию?

  1. Вложенные циклы хорошо подходят для работы с таблицами:

    • Сравнение значений между двумя таблицами.
    • Построение сложных комбинаций данных.
  2. Рекурсия лучше для:

    • Вычисления последовательностей (например, факториалов, Фибоначчи).
    • Работы с иерархическими структурами (например, дерево категорий товаров).

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

Вложенные циклы иногда являются "дорогими" в плане производительности, особенно при работе с большими таблицами. Используйте их только когда невозможно достичь результата обычными средствами SQL.

При использовании рекурсии убедитесь, что у вас есть чёткое "условие остановки". Без него вы получите бесконечный вызов функции и, скорее всего, ошибку переполнения стека.

Сложные вложенные конструкции могут затруднить отладку. Помогайте себе RAISE NOTICE для вывода промежуточных результатов.

2
Задача
SQL SELF, 51 уровень, 3 лекция
Недоступна
Простая таблица умножения с помощью вложенных циклов
Простая таблица умножения с помощью вложенных циклов
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ