Parámetros
Los procedimientos almacenados pueden recibir y devolver datos a través de parámetros. MySQL soporta tres tipos: IN para entrada, OUT para salida e INOUT para ambos.
Los parámetros son lo que convierte un procedimiento almacenado de un bloque de código rígido en una herramienta flexible y reutilizable. Sin parámetros, necesitarías un procedimiento diferente para cada variación de una consulta. Con parámetros, un solo procedimiento puede adaptarse a múltiples situaciones: buscar productos por categoría, calcular descuentos con diferentes porcentajes, o generar reportes para distintos rangos de fechas. Los procedimientos también pueden usar variables locales para cálculos internos.
Sintaxis
CREATE PROCEDURE nombre(
IN parametro_entrada tipo,
OUT parametro_salida tipo,
INOUT parametro_bidireccional tipo
)
BEGIN
-- ...
ENDCada parámetro se define con su dirección (IN, OUT o INOUT), un nombre y un tipo de dato MySQL. Puedes tener tantos parámetros como necesites, aunque en la práctica más de 5 o 6 empiezan a hacer el procedimiento difícil de usar. Si necesitas pasar muchos datos, considera usar una tabla temporal o una tabla de parámetros en su lugar.
Parámetro IN
IN es el tipo por defecto. El procedimiento recibe una copia del valor; los cambios dentro del procedimiento no afectan a la variable original del llamador. Funciona de manera similar a pasar argumentos por valor en lenguajes de programación como Java o C.
La mayoría de los parámetros que usarás en la práctica serán IN: filtros de búsqueda, IDs de registros, valores para cálculos, etc.
DELIMITER //
CREATE PROCEDURE buscar_productos(IN texto VARCHAR(100))
BEGIN
SELECT nombre, precio
FROM productos
WHERE nombre LIKE CONCAT('%', texto, '%')
ORDER BY precio;
END //
DELIMITER ;CALL buscar_productos('Samsung');| nombre | precio |
|---|---|
| Samsung Galaxy S24 | 899.99 |
| Samsung TV QLED 55" | 749.00 |
Si omites la palabra clave IN, MySQL la asume por defecto. Estas dos declaraciones son equivalentes:
-- IN es implícito, estas dos formas son equivalentes:
-- IN texto VARCHAR(100)
-- texto VARCHAR(100)Sin embargo, es buena práctica escribir IN explícitamente para que quede claro al leer el código. Cuando un procedimiento tiene mezcla de IN, OUT e INOUT, la legibilidad se beneficia enormemente de ser explícito en todos los parámetros.
Parámetro OUT
OUT permite al procedimiento devolver un valor al llamador. Dentro del procedimiento, el parámetro comienza siempre como NULL, independientemente del valor que se le pase al llamar. El procedimiento debe asignarle un valor con SET o SELECT INTO.
Los parámetros OUT son la forma principal de que un procedimiento "devuelva" resultados calculados al código que lo llamó. A diferencia de las funciones almacenadas, un procedimiento puede tener múltiples parámetros OUT, lo que le permite devolver varios valores a la vez.
DELIMITER //
CREATE PROCEDURE contar_por_categoria(
IN cat_id INT,
OUT total INT
)
BEGIN
SELECT COUNT(*) INTO total
FROM productos
WHERE categoria_id = cat_id AND activo = TRUE;
END //
DELIMITER ;Para recibir el valor OUT, se pasan variables de sesión (con prefijo @) en la llamada:
CALL contar_por_categoria(6, @total);
SELECT @total AS total_electronica;| total_electronica |
|---|
| 4 |
Es importante entender que los parámetros OUT solo se pueden recibir a través de variables de sesión cuando llamas al procedimiento desde el cliente MySQL. Cuando llamas a un procedimiento desde otro procedimiento, puedes usar variables locales normales. Desde lenguajes de programación, cada driver tiene su propia forma de manejar parámetros OUT (en JDBC se usa registerOutParameter(), en PHP con PDO se usa bindParam() con PDO::PARAM_INPUT_OUTPUT).
Parámetro INOUT
INOUT combina las características de IN y OUT. El procedimiento recibe el valor actual de la variable y puede modificarlo. El valor modificado queda disponible para el llamador después de la ejecución.
Piénsalo como pasar una variable "por referencia": el procedimiento puede leer el valor original y sobrescribirlo. Esto es útil cuando quieres transformar un valor in-place sin necesidad de un parámetro adicional.
DELIMITER //
CREATE PROCEDURE aplicar_descuento(INOUT precio DECIMAL(10,2), IN porcentaje INT)
BEGIN
SET precio = precio - (precio * porcentaje / 100);
END //
DELIMITER ;SET @mi_precio = 1299.99;
CALL aplicar_descuento(@mi_precio, 15);
SELECT @mi_precio AS precio_con_descuento;| precio_con_descuento |
|---|
| 1104.99 |
Observa que la variable @mi_precio entró con el valor 1299.99 y salió con 1104.99. El procedimiento la leyó, calculó el descuento y la modificó directamente.
En la práctica, INOUT se usa con menos frecuencia que IN y OUT. Muchos desarrolladores prefieren usar un parámetro IN para el valor original y un parámetro OUT separado para el resultado, ya que esto hace más explícito el flujo de datos. Sin embargo, INOUT es la opción natural cuando el concepto es "toma este valor y transfórmalo".
Múltiples parámetros OUT
Un procedimiento puede tener varios parámetros OUT para devolver múltiples valores calculados. Esto es uno de los puntos fuertes de los procedimientos frente a las funciones, que solo pueden devolver un único valor:
DELIMITER //
CREATE PROCEDURE estadisticas_categoria(
IN cat_id INT,
OUT total_prod INT,
OUT precio_min DECIMAL(10,2),
OUT precio_max DECIMAL(10,2),
OUT precio_medio DECIMAL(10,2)
)
BEGIN
SELECT COUNT(*), MIN(precio), MAX(precio), AVG(precio)
INTO total_prod, precio_min, precio_max, precio_medio
FROM productos
WHERE categoria_id = cat_id AND activo = TRUE;
END //
DELIMITER ;CALL estadisticas_categoria(6, @total, @min, @max, @avg);
SELECT @total AS productos, @min AS minimo, @max AS maximo, @avg AS promedio;| productos | minimo | maximo | promedio |
|---|---|---|---|
| 4 | 599.99 | 1299.99 | 849.74 |
Las variables de sesión @total, @min, @max y @avg persisten en la sesión después de la llamada, así que puedes usarlas en sentencias posteriores. Esto es práctico para encadenar lógica: por ejemplo, llamar a un procedimiento que calcule estadísticas y usar esos valores en una consulta posterior.
Comparación de tipos
| Tipo | Dirección | Valor inicial dentro del procedimiento | Uso típico |
|---|---|---|---|
| IN | Entrada | Valor pasado por el llamador | Filtros, criterios de búsqueda, IDs |
| OUT | Salida | NULL (siempre) | Devolver resultados calculados |
| INOUT | Ambos | Valor pasado por el llamador | Transformar un valor |
Ejemplo completo
Este procedimiento reúne todos los conceptos: usa un parámetro IN para recibir datos, OUT para devolver resultados y lógica condicional para validar la operación antes de ejecutarla. Es representativo de lo que encontrarás en sistemas de producción, donde los procedimientos no solo ejecutan consultas sino que implementan reglas de negocio:
DELIMITER //
CREATE PROCEDURE procesar_pedido(
IN cliente INT,
IN producto INT,
IN cantidad INT,
OUT pedido_id INT,
OUT mensaje VARCHAR(200)
)
BEGIN
DECLARE stock_actual INT;
DECLARE precio_prod DECIMAL(10,2);
-- Verificar stock
SELECT stock, precio INTO stock_actual, precio_prod
FROM productos WHERE id = producto;
IF stock_actual >= cantidad THEN
-- Crear pedido
INSERT INTO pedidos (cliente_id, fecha, total, estado)
VALUES (cliente, CURDATE(), precio_prod * cantidad, 'pendiente');
SET pedido_id = LAST_INSERT_ID();
-- Añadir detalle
INSERT INTO detalle_pedidos (pedido_id, producto_id, cantidad, precio_unitario)
VALUES (pedido_id, producto, cantidad, precio_prod);
-- Actualizar stock
UPDATE productos SET stock = stock - cantidad WHERE id = producto;
SET mensaje = CONCAT('Pedido #', pedido_id, ' creado correctamente');
ELSE
SET pedido_id = 0;
SET mensaje = CONCAT('Stock insuficiente. Disponible: ', stock_actual);
END IF;
END //
DELIMITER ;El procedimiento verifica el stock antes de procesar, evitando pedidos que no se pueden cumplir. La sentencia IF controla el flujo según el stock disponible. Este tipo de validación dentro del procedimiento es una de sus mayores ventajas: la lógica de negocio se ejecuta atómicamente en el servidor, sin ventanas de tiempo entre la verificación del stock y la creación del pedido donde otro proceso pudiera agotar las existencias.
Errores comunes
Olvidar el prefijo @ en variables de sesión. Al llamar al procedimiento, los parámetros OUT e INOUT deben recibir variables de sesión con @. Si pasas un literal o una expresión, MySQL genera un error porque no tiene dónde almacenar el resultado.
Asumir que OUT conserva el valor pasado. Un parámetro OUT siempre empieza como NULL dentro del procedimiento, sin importar qué valor tenía la variable de sesión antes de la llamada. Si necesitas leer el valor existente y modificarlo, usa INOUT.
Demasiados parámetros. Un procedimiento con 10 o más parámetros es difícil de llamar correctamente (es fácil confundir el orden). Considera agrupar los datos en una tabla temporal o dividir la lógica en varios procedimientos más pequeños.
Limpieza
DROP PROCEDURE IF EXISTS buscar_productos;
DROP PROCEDURE IF EXISTS contar_por_categoria;
DROP PROCEDURE IF EXISTS aplicar_descuento;
DROP PROCEDURE IF EXISTS estadisticas_categoria;
DROP PROCEDURE IF EXISTS procesar_pedido;En el siguiente artículo veremos las variables dentro de procedimientos almacenados.
Escrito por Eduardo Lázaro
