CodeGym /Kursy /SQL SELF /Zagnieżdżone pętle i rekurencja

Zagnieżdżone pętle i rekurencja

SQL SELF
Poziom 51 , Lekcja 3
Dostępny

Zagnieżdżone pętle to takie pętle, które działają w środku innych pętli. Wyobraź sobie sekcję pytań na rozmowie o pracę, gdzie jedno pytanie prowadzi do kolejnych dopytań. Logika zagnieżdżonych pętli jest taka: zewnętrzna pętla robi iterację, a wewnętrzna przechodzi przez swoje wartości dla każdej iteracji tej zewnętrznej.

Przykład: tabliczka mnożenia

Napiszemy funkcję, która tworzy tabliczkę mnożenia dla liczb od 1 do 5. To klasyczny przykład, gdzie używa się zagnieżdżonych pętli:

CREATE OR REPLACE FUNCTION generate_multiplication_table()
RETURNS VOID AS $$
BEGIN
  FOR i IN 1..5 LOOP -- Zewnętrzna pętla
    FOR j IN 1..5 LOOP -- Wewnętrzna pętla
      RAISE NOTICE '% x % = %', i, j, i * j; -- Logujemy wynik
    END LOOP;
  END LOOP;
END;
$$ LANGUAGE plpgsql;

-- Wywołanie funkcji:
SELECT generate_multiplication_table();

Jak to działa:

  1. Zewnętrzna pętla przechwytuje wartości i od 1 do 5.
  2. Dla każdej wartości i, wewnętrzna pętla bierze wartości j od 1 do 5.
  3. Na każdym kroku łączymy i i j, żeby policzyć wynik mnożenia.
  4. Wynik wygląda jak mini-tabliczka:
   1 x 1 = 1
   1 x 2 = 2
   ...
   5 x 5 = 25

Praktyka: szukanie przecięć w dwóch tabelach

Teraz zróbmy bardziej "życiowy" przykład. Wyobraź sobie, że mamy dwie tabele:

  • students (studenci, ich imiona),
  • courses (kursy, na które są zapisani).

Chcemy znaleźć studentów, którzy są zapisani na więcej niż jeden kurs. Użyjemy zagnieżdżonych pętli:

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;

-- Wywołanie funkcji:
SELECT * FROM find_students_with_multiple_courses();

Rekurencja

Rekurencja to sytuacja, gdy funkcja wywołuje samą siebie. To trochę jakbyś poprosił kumpla o wyjaśnienie SQL, a on kazał Ci przeczytać dokumentację, która odsyła do tego samego wykładu… Nie myl rekurencji z nieskończoną pętlą. Rekurencja zawsze ma "warunek stopu" (moment, w którym funkcja przestaje się wywoływać).

Przykład: liczenie silni liczby

Silnia liczby n to iloczyn wszystkich liczb od 1 do n. Na przykład, silnia 5 (oznaczana jako 5!) to 5 * 4 * 3 * 2 * 1 = 120. Tak to można ogarnąć rekurencyjnie:

CREATE OR REPLACE FUNCTION calculate_factorial(n INTEGER)
RETURNS INTEGER AS $$
BEGIN
  -- Warunek stopu: silnia 0 lub 1 to 1
  IF n = 0 OR n = 1 THEN
    RETURN 1;
  END IF;

  -- Rekurencyjne wywołanie funkcji
  RETURN n * calculate_factorial(n - 1);
END;
$$ LANGUAGE plpgsql;

-- Wywołanie funkcji:
SELECT calculate_factorial(5); -- Wynik: 120

Jak to działa:

  1. Jeśli n to 0 lub 1, zwracamy 1.
  2. Jeśli n > 1, funkcja wywołuje samą siebie z argumentem n - 1 i mnoży to przez n.
  3. W ten sposób wywołania się "nakładają", a potem zwijają w jedną stronę.

Praktyczny przykład: liczby Fibonacciego

Liczby Fibonacciego to ciąg liczb, gdzie każda liczba jest sumą dwóch poprzednich. Sekwencja zaczyna się tak: 0, 1, 1, 2, 3, 5, 8....

Napiszmy funkcję, która liczy n-tą liczbę w tym ciągu:

CREATE OR REPLACE FUNCTION fibonacci(n INTEGER)
RETURNS INTEGER AS $$
BEGIN
  -- Warunek stopu: pierwsze dwie liczby są znane
  IF n = 0 THEN
    RETURN 0;
  ELSIF n = 1 THEN
    RETURN 1;
  END IF;

  -- Rekurencyjne wywołanie funkcji
  RETURN fibonacci(n - 1) + fibonacci(n - 2);
END;
$$ LANGUAGE plpgsql;

-- Wywołanie funkcji:
SELECT fibonacci(6); -- Wynik: 8

Kiedy używać zagnieżdżonych pętli i rekurencji?

  1. Zagnieżdżone pętle są spoko do pracy z tabelami:

    • Porównywanie wartości między dwiema tabelami.
    • Budowanie złożonych kombinacji danych.
  2. Rekurencja lepiej się sprawdza przy:

    • Liczeniu sekwencji (np. silnie, Fibonacciego).
    • Pracy z hierarchicznymi strukturami (np. drzewo kategorii produktów).

Typowe wtopy

Zagnieżdżone pętle potrafią być "drogie" wydajnościowo, szczególnie przy dużych tabelach. Używaj ich tylko wtedy, gdy nie da się osiągnąć efektu zwykłym SQL-em.

Przy rekurencji upewnij się, że masz jasno określony "warunek stopu". Bez tego złapiesz nieskończone wywołania funkcji i pewnie błąd przepełnienia stosu.

Złożone zagnieżdżone konstrukcje mogą utrudnić debugowanie. Pomagaj sobie RAISE NOTICE, żeby wyświetlać wyniki pośrednie.

Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION