Qualche livello fa abbiamo già accennato a funzioni e procedure in PostgreSQL. Ora è il momento di approfondire un po’ di più.
Funzioni e procedure possono lavorare in modo indipendente, ma spesso la loro interazione determina il successo dell’intero sistema. Il bello è che puoi chiamare una funzione dall’altra, passare dati e persino ottenere il risultato dell’esecuzione.
Funzioni vs Procedure: qual è la differenza?
Facciamo un ripasso veloce su cosa distingue le funzioni dalle procedure in PostgreSQL:
Funzioni (
FUNCTION):- Restituiscono valori.
- Si possono usare dentro
SELECT. - Spesso servono per calcoli o trasformazioni di dati.
Procedure (
PROCEDURE):- Non restituiscono valori direttamente.
- Si usano per operazioni come inserimento, aggiornamento o cancellazione di dati.
- Si chiamano con il comando
CALL.
Passaggio di dati tra funzioni
Andando sul pratico, partiamo da un esempio base di passaggio dati tra funzione e procedura. In sostanza, i dati tra funzioni si passano tramite parametri e valori di ritorno.
Ecco come si chiama una funzione dentro un’altra funzione:
CREATE OR REPLACE FUNCTION get_student_name(student_id INT)
RETURNS TEXT AS $$
DECLARE
student_name TEXT;
BEGIN
-- Prendiamo il nome dello studente tramite il suo ID
SELECT name INTO student_name FROM students WHERE id = student_id;
-- Restituiamo il nome
RETURN student_name;
END;
$$ LANGUAGE plpgsql;
Questa funzione si può chiamare da un’altra funzione:
CREATE OR REPLACE FUNCTION welcome_student(student_id INT)
RETURNS TEXT AS $$
DECLARE
message TEXT;
BEGIN
-- Otteniamo il nome dello studente tramite un’altra funzione
message := 'Benvenuto, ' || get_student_name(student_id) || '!';
-- Restituiamo il messaggio di benvenuto
RETURN message;
END;
$$ LANGUAGE plpgsql;
- La funzione
get_student_namerestituisce il nome dello studente tramite il suo identificativo (student_id). - Nell’altra funzione —
welcome_student— questo nome viene usato per creare un messaggio di benvenuto.
Nota: L’estrazione dei dati tramite SELECT INTO salva il risultato della query in una variabile PL/pgSQL.
Esempio di chiamata di procedure da funzioni
Vediamo ora come chiamare una procedura da una funzione. Supponiamo di avere una procedura che registra l’orario di accesso di uno studente al sistema:
CREATE OR REPLACE PROCEDURE log_student_entry(student_id INT)
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO log_entries(student_id, entry_time)
VALUES (student_id, NOW());
END;
$$;
Ora chiamiamo questa procedura da una funzione, dove registriamo l’accesso e restituiamo un messaggio:
CREATE OR REPLACE FUNCTION student_login(student_id INT)
RETURNS TEXT AS $$
BEGIN
-- Chiamiamo la procedura per il logging
CALL log_student_entry(student_id);
-- Restituiamo il messaggio
RETURN 'Accesso studente registrato con successo.';
END;
$$ LANGUAGE plpgsql;
Esempi pratici di interazione
Esempio 1: calcolo del totale ordine e logging
Immagina di lavorare con un sistema di ordini online. Per calcolare il totale di un ordine hai una funzione:
CREATE OR REPLACE FUNCTION calculate_order_total(order_id INT)
RETURNS NUMERIC AS $$
DECLARE
total NUMERIC;
BEGIN
-- Sommiamo tutte le voci dell’ordine
SELECT SUM(price * quantity) INTO total
FROM order_items
WHERE order_id = order_id;
RETURN total;
END;
$$ LANGUAGE plpgsql;
Per salvare il totale dell’ordine si usa una procedura:
CREATE OR REPLACE PROCEDURE log_order_total(order_id INT, total NUMERIC)
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO order_totals(order_id, total)
VALUES (order_id, total);
END;
$$;
Ora le colleghiamo insieme:
CREATE OR REPLACE FUNCTION process_order(order_id INT)
RETURNS TEXT AS $$
DECLARE
total NUMERIC;
BEGIN
-- Chiamiamo la funzione per calcolare il totale
total := calculate_order_total(order_id);
-- Logghiamo il totale tramite la procedura
CALL log_order_total(order_id, total);
RETURN 'Ordine processato con successo.';
END;
$$ LANGUAGE plpgsql;
Esempio 2: ottenere il rating massimo dello studente e aggiornare il profilo
Funzione per ottenere il rating massimo:
CREATE OR REPLACE FUNCTION get_highest_rating(student_id INT)
RETURNS INT AS $$
DECLARE
max_rating INT;
BEGIN
-- Troviamo il rating massimo dello studente
SELECT MAX(rating) INTO max_rating
FROM ratings
WHERE student_id = student_id;
RETURN max_rating;
END;
$$ LANGUAGE plpgsql;
Procedura per aggiornare il profilo dello studente:
CREATE OR REPLACE PROCEDURE update_student_profile(student_id INT, max_rating INT)
LANGUAGE plpgsql
AS $$
BEGIN
UPDATE students
SET highest_rating = max_rating
WHERE id = student_id;
END;
$$;
Funzione per chiamare queste operazioni:
CREATE OR REPLACE FUNCTION refresh_student_profile(student_id INT)
RETURNS TEXT AS $$
DECLARE
max_rating INT;
BEGIN
-- Otteniamo il rating massimo
max_rating := get_highest_rating(student_id);
-- Aggiorniamo il profilo dello studente
CALL update_student_profile(student_id, max_rating);
RETURN 'Profilo aggiornato con successo.';
END;
$$ LANGUAGE plpgsql;
Errori tipici nell’interazione
Uno degli errori più comuni è la non corrispondenza dei tipi di dato tra funzione e procedura. Ad esempio, se la tua procedura si aspetta un parametro di tipo NUMERIC, ma tu passi un INTEGER, PostgreSQL segnalerà un errore di tipo. Controlla sempre che i tipi coincidano.
Un altro errore è la chiamata ciclica tra funzioni, quando la funzione A chiama la funzione B, che a sua volta richiama A. Questo porta a chiamate infinite e crash del sistema.
Importanza pratica
Perché ci serve questa interazione? Nella vita reale funzioni e procedure sono come “mattoncini” di sistemi complessi. Permettono di dividere il codice in parti indipendenti, facilitando il debug, il riutilizzo e i test. Ad esempio:
- Al colloquio potrebbero chiederti di scrivere una funzione che chiama una procedura per eseguire un’operazione complessa. Dimostrare queste skill pratiche è un bel punto a favore.
- Nello sviluppo di applicazioni reali, come e-commerce, sistemi di logging o CRM, saper organizzare bene la logica tramite l’interazione tra funzioni e procedure rende il codice molto più semplice.
Per approfondire l’interazione tra funzioni e procedure puoi dare un’occhiata alla documentazione ufficiale di PL/pgSQL.
GO TO FULL VERSION