I cicli annidati sono cicli che girano dentro altri cicli. Immagina una sezione di domande a un colloquio, dove una domanda porta a nuove domande di approfondimento. La logica dei cicli annidati è questa: il ciclo esterno fa un giro, e il ciclo interno fa i suoi giri per ogni iterazione del ciclo esterno.
Esempio: tabella di moltiplicazione
Scriviamo una funzione che crea una tabella di moltiplicazione per i numeri da 1 a 5. È un esempio classico dove si usano i cicli annidati:
CREATE OR REPLACE FUNCTION generate_multiplication_table()
RETURNS VOID AS $$
BEGIN
FOR i IN 1..5 LOOP -- Ciclo esterno
FOR j IN 1..5 LOOP -- Ciclo interno
RAISE NOTICE '% x % = %', i, j, i * j; -- Logghiamo il risultato
END LOOP;
END LOOP;
END;
$$ LANGUAGE plpgsql;
-- Chiamata della funzione:
SELECT generate_multiplication_table();
Logica di funzionamento:
- Il ciclo esterno prende i valori di
ida 1 a 5. - Per ogni valore di
i, il ciclo interno prende i valori dijda 1 a 5. - Ad ogni passo si combinano i valori di
iejper calcolare il risultato della moltiplicazione. - L’output sembra una mini-tabella:
1 x 1 = 1
1 x 2 = 2
...
5 x 5 = 25
Pratica: trovare le intersezioni tra due tabelle
Ora facciamo un esempio più “reale”. Immagina di avere due tabelle:
students(studenti, i loro nomi),courses(corsi a cui sono iscritti).
Vogliamo trovare gli studenti iscritti a più di un corso. Usiamo i cicli annidati:
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;
-- Chiamata della funzione:
SELECT * FROM find_students_with_multiple_courses();
Ricorsione
La ricorsione è quando una funzione chiama se stessa. È come se chiedessi a un amico di spiegarti SQL e lui ti dicesse di leggere la documentazione che rimanda a questa stessa lezione… Non confondere la ricorsione con un ciclo infinito. La ricorsione ha sempre una “condizione di stop” (il punto in cui la funzione smette di chiamare se stessa).
Esempio: calcolo del fattoriale di un numero
Il fattoriale di un numero n è il prodotto di tutti i numeri da 1 a n. Per esempio, il fattoriale di 5 (si scrive 5!) è 5 * 4 * 3 * 2 * 1 = 120. Ecco come si può fare con la ricorsione:
CREATE OR REPLACE FUNCTION calculate_factorial(n INTEGER)
RETURNS INTEGER AS $$
BEGIN
-- Condizione di stop: il fattoriale di 0 o 1 è 1
IF n = 0 OR n = 1 THEN
RETURN 1;
END IF;
-- Chiamata ricorsiva della funzione
RETURN n * calculate_factorial(n - 1);
END;
$$ LANGUAGE plpgsql;
-- Chiamata della funzione:
SELECT calculate_factorial(5); -- Risultato: 120
Logica di funzionamento:
- Se
nè 0 o 1, ritorna 1. - Se
n > 1, la funzione chiama se stessa conn - 1e moltiplica il risultato pern. - Così le chiamate si “accumulano” e poi si risolvono tutte in una direzione.
Esempio pratico: numeri di Fibonacci
I numeri di Fibonacci sono una sequenza in cui ogni numero è la somma dei due precedenti. La sequenza inizia così: 0, 1, 1, 2, 3, 5, 8....
Scriviamo una funzione per calcolare l’n-esimo numero della sequenza:
CREATE OR REPLACE FUNCTION fibonacci(n INTEGER)
RETURNS INTEGER AS $$
BEGIN
-- Condizione di stop: i primi due numeri sono noti
IF n = 0 THEN
RETURN 0;
ELSIF n = 1 THEN
RETURN 1;
END IF;
-- Chiamata ricorsiva della funzione
RETURN fibonacci(n - 1) + fibonacci(n - 2);
END;
$$ LANGUAGE plpgsql;
-- Chiamata della funzione:
SELECT fibonacci(6); -- Risultato: 8
Quando usare cicli annidati e ricorsione?
I cicli annidati sono ottimi per lavorare con le tabelle:
- Confrontare valori tra due tabelle.
- Costruire combinazioni di dati complesse.
La ricorsione è meglio per:
- Calcolare sequenze (tipo fattoriali, Fibonacci).
- Lavorare con strutture gerarchiche (tipo un albero di categorie di prodotti).
Errori tipici
I cicli annidati a volte sono “costosi” in termini di performance, soprattutto con tabelle grandi. Usali solo quando non puoi ottenere il risultato con i normali strumenti SQL.
Quando usi la ricorsione, assicurati di avere una “condizione di stop” chiara. Senza di essa avrai chiamate infinite e probabilmente un errore di stack overflow.
Costruzioni annidate complesse possono rendere il debug difficile. Aiutati con RAISE NOTICE per stampare risultati intermedi.
GO TO FULL VERSION