Hace un par de niveles ya tocamos el tema de procedimientos y funciones en PostgreSQL. Ahora toca profundizar más.
Las funciones y los procedimientos pueden funcionar por separado, pero normalmente su interacción es lo que define el éxito de todo el sistema. Lo más cómodo es que puedes llamar funciones unas desde otras, pasar datos e incluso obtener el resultado de la ejecución.
Funciones vs Procedimientos: ¿en qué se diferencian?
Vamos a recordar en qué se diferencian las funciones de los procedimientos en PostgreSQL:
Funciones (
FUNCTION):- Devuelven valores.
- Puedes usarlas en
SELECT. - Se usan mucho para cálculos o transformación de datos.
Procedimientos (
PROCEDURE):- No devuelven valores directamente.
- Se usan para operaciones como insertar, actualizar o borrar datos.
- Se llaman con el comando
CALL.
Paso de datos entre funciones
Vamos a la práctica, empezando con un ejemplo básico de cómo pasar datos entre una función y un procedimiento. Básicamente, el paso de datos entre funciones se hace a través de parámetros y valores devueltos.
Así se ve una llamada a una función dentro de otra función:
CREATE OR REPLACE FUNCTION get_student_name(student_id INT)
RETURNS TEXT AS $$
DECLARE
student_name TEXT;
BEGIN
-- Sacamos el nombre del estudiante por su ID
SELECT name INTO student_name FROM students WHERE id = student_id;
-- Devolvemos el nombre
RETURN student_name;
END;
$$ LANGUAGE plpgsql;
Esta función se puede llamar desde otra función:
CREATE OR REPLACE FUNCTION welcome_student(student_id INT)
RETURNS TEXT AS $$
DECLARE
message TEXT;
BEGIN
-- Obtenemos el nombre del estudiante usando otra función
message := '¡Bienvenido, ' || get_student_name(student_id) || '!';
-- Devolvemos el saludo
RETURN message;
END;
$$ LANGUAGE plpgsql;
- La función
get_student_namedevuelve el nombre del estudiante por su identificador (student_id). - En la otra función —
welcome_student— ese nombre se usa para crear un mensaje de bienvenida.
Nota: Sacar datos usando SELECT INTO guarda el resultado de la consulta en una variable de PL/pgSQL.
Ejemplo de llamada a procedimientos desde funciones
Ahora vamos a ver cómo llamar un procedimiento desde una función. Imagina que tienes un procedimiento que registra la hora de entrada de un estudiante 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;
$$;
Ahora llamamos a este procedimiento desde una función, donde se registra la entrada y se devuelve un mensaje:
CREATE OR REPLACE FUNCTION student_login(student_id INT)
RETURNS TEXT AS $$
BEGIN
-- Llamamos al procedimiento para registrar el log
CALL log_student_entry(student_id);
-- Devolvemos el mensaje
RETURN 'Inicio de sesión del estudiante registrado correctamente.';
END;
$$ LANGUAGE plpgsql;
Ejemplos prácticos de interacción
Ejemplo 1: cálculo del total del pedido y registro del pedido
Imagina que trabajas con un sistema de pedidos online. Para calcular el total de un pedido tienes una función:
CREATE OR REPLACE FUNCTION calculate_order_total(order_id INT)
RETURNS NUMERIC AS $$
DECLARE
total NUMERIC;
BEGIN
-- Sumamos todas las posiciones del pedido
SELECT SUM(price * quantity) INTO total
FROM order_items
WHERE order_id = order_id;
RETURN total;
END;
$$ LANGUAGE plpgsql;
Para guardar el total del pedido se usa un procedimiento:
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;
$$;
Ahora los unimos:
CREATE OR REPLACE FUNCTION process_order(order_id INT)
RETURNS TEXT AS $$
DECLARE
total NUMERIC;
BEGIN
-- Llamamos a la función para calcular el total
total := calculate_order_total(order_id);
-- Registramos el total usando el procedimiento
CALL log_order_total(order_id, total);
RETURN 'Pedido procesado correctamente.';
END;
$$ LANGUAGE plpgsql;
Ejemplo 2: obtener la máxima puntuación del estudiante y actualizar el perfil
Función para obtener la máxima puntuación:
CREATE OR REPLACE FUNCTION get_highest_rating(student_id INT)
RETURNS INT AS $$
DECLARE
max_rating INT;
BEGIN
-- Buscamos la máxima puntuación del estudiante
SELECT MAX(rating) INTO max_rating
FROM ratings
WHERE student_id = student_id;
RETURN max_rating;
END;
$$ LANGUAGE plpgsql;
Procedimiento para actualizar el perfil del estudiante:
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;
$$;
Función para llamar a estas operaciones:
CREATE OR REPLACE FUNCTION refresh_student_profile(student_id INT)
RETURNS TEXT AS $$
DECLARE
max_rating INT;
BEGIN
-- Obtenemos la máxima puntuación
max_rating := get_highest_rating(student_id);
-- Actualizamos el perfil del estudiante
CALL update_student_profile(student_id, max_rating);
RETURN 'Perfil actualizado correctamente.';
END;
$$ LANGUAGE plpgsql;
Errores típicos en la interacción
Uno de los errores más comunes es la incompatibilidad de tipos de datos entre la función y el procedimiento. Por ejemplo, si tu procedimiento espera un parámetro de tipo NUMERIC y le pasas un INTEGER, PostgreSQL te va a avisar del error de tipos. Siempre revisa que los tipos de datos coincidan.
Otro error es el llamado cíclico de funciones, cuando la función A llama a la función B, y esta a su vez vuelve a llamar a A. Esto lleva a llamadas infinitas y a que el sistema se caiga.
Importancia práctica
¿Para qué nos sirve esta interacción? En la vida real, las funciones y los procedimientos funcionan como "bloques de construcción" de sistemas complejos. Permiten dividir el código en partes independientes, lo que facilita el debug, la reutilización y las pruebas. Por ejemplo:
- En una entrevista te pueden pedir que escribas una función que llame a un procedimiento para realizar una operación compleja. Demostrar habilidades prácticas de interacción será un gran plus.
- Al desarrollar aplicaciones reales, como tiendas online, sistemas de logs o CRM, saber organizar bien la funcionalidad a través de la interacción entre funciones y procedimientos simplifica mucho el código.
Para seguir aprendiendo sobre la interacción entre funciones y procedimientos puedes mirar la documentación oficial de PL/pgSQL.
GO TO FULL VERSION