Introducción a los procedimientos almacenados
Un procedimiento almacenado es un conjunto de sentencias SQL que se guarda en el servidor de base de datos y se ejecuta con una sola llamada. Los procedimientos pueden recibir parámetros, usar variables, condicionales, bucles y devolver resultados.
Piensa en un procedimiento almacenado como una receta que le entregas al cocinero: en lugar de darle instrucciones paso a paso cada vez que quieres un plato, escribes la receta una vez y después simplemente le dices "prepara el plato número 3". El servidor MySQL es ese cocinero: recibe la receta (el procedimiento), la guarda y la ejecuta cada vez que se la pides.
¿Por qué existen los procedimientos almacenados?
Cuando una aplicación necesita interactuar con la base de datos, normalmente envía sentencias SQL desde el código del servidor (PHP, Python, Java, Node.js...). Para operaciones simples como un SELECT o un INSERT, esto funciona bien. Pero cuando la lógica de negocio requiere varias consultas coordinadas — verificar stock, crear un pedido, actualizar inventario, registrar un log — enviar cada sentencia individualmente desde la aplicación tiene varios problemas: múltiples viajes de red, lógica de negocio dispersa entre la aplicación y la base de datos, y riesgo de inconsistencias si algo falla a mitad del proceso.
Los procedimientos almacenados resuelven estos problemas encapsulando toda esa lógica en un solo objeto que vive dentro de MySQL. La aplicación hace una sola llamada y el servidor se encarga del resto.
¿Qué es un procedimiento almacenado?
Un procedimiento almacenado es un programa que vive dentro de MySQL. Se crea una vez con CREATE PROCEDURE, se compila y se almacena en el catálogo de la base de datos. A partir de ese momento, cualquier usuario o aplicación con los permisos adecuados puede ejecutarlo con la sentencia CALL.
En lugar de enviar varias sentencias SQL desde tu aplicación, encapsulas la lógica en un procedimiento y lo llamas por su nombre:
-- Sin procedimiento: múltiples consultas desde la aplicación
SELECT * FROM productos WHERE categoria_id = 6;
SELECT AVG(precio) FROM productos WHERE categoria_id = 6;
SELECT COUNT(*) FROM productos WHERE categoria_id = 6;
-- Con procedimiento: una sola llamada
CALL resumen_categoria(6);La diferencia es significativa. En el primer caso, la aplicación envía tres consultas separadas al servidor, espera tres respuestas y tiene que combinar los resultados ella misma. En el segundo caso, envía una sola instrucción y el servidor ejecuta toda la lógica internamente, devolviendo los resultados ya procesados.
Primer ejemplo
Para crear un procedimiento se usa la sentencia CREATE PROCEDURE junto con DELIMITER (que veremos en detalle en el siguiente artículo). El cuerpo del procedimiento va entre BEGIN y END:
DELIMITER //
CREATE PROCEDURE contar_productos()
BEGIN
SELECT COUNT(*) AS total_productos FROM productos;
END //
DELIMITER ;Una vez creado, se ejecuta con CALL:
CALL contar_productos();| total_productos |
|---|
| 30 |
Este es un ejemplo mínimo, pero los procedimientos pueden ser mucho más complejos: recibir parámetros de entrada, devolver valores de salida, declarar variables internas, usar condicionales IF/CASE, iterar con bucles LOOP/WHILE/REPEAT, recorrer resultados con cursores y manejar errores con handlers. A lo largo de esta sección iremos cubriendo cada una de estas capacidades.
Ventajas
Reducción del tráfico de red. En lugar de enviar múltiples sentencias SQL a través de la red, la aplicación envía solo CALL nombre_procedimiento(). Toda la lógica se ejecuta dentro del servidor de base de datos, lo que elimina los viajes de ida y vuelta entre la aplicación y MySQL. En entornos donde la latencia de red es un factor (servidores en diferentes regiones, conexiones lentas), esta diferencia puede ser notable.
Reutilización. Un procedimiento almacenado se escribe una vez y puede ser llamado desde múltiples aplicaciones, microservicios o herramientas de administración. Si tienes una aplicación web en PHP, una API en Node.js y un script de reportes en Python, todas pueden usar el mismo procedimiento para crear pedidos o calcular estadísticas, garantizando que la lógica sea consistente en todos los puntos de acceso.
Seguridad. Los procedimientos permiten crear una capa de abstracción sobre las tablas. Puedes dar a un usuario permiso para ejecutar un procedimiento (GRANT EXECUTE) sin darle acceso directo a las tablas subyacentes. Esto es especialmente útil en entornos con múltiples niveles de acceso: los desarrolladores de la aplicación solo necesitan conocer los procedimientos disponibles, no la estructura interna de las tablas.
Mantenimiento centralizado. Cuando la lógica de negocio vive en procedimientos almacenados, un cambio en esa lógica solo requiere modificar el procedimiento en la base de datos. No hace falta desplegar una nueva versión de la aplicación, reiniciar servidores ni coordinar actualizaciones en múltiples servicios. Esto puede ser una ventaja decisiva en sistemas críticos donde minimizar el tiempo de inactividad es prioritario.
Rendimiento. MySQL compila y cachea el plan de ejecución de los procedimientos almacenados. Aunque la mejora de rendimiento respecto a sentencias SQL individuales no es tan dramática como en otros motores de base de datos (SQL Server, Oracle), sí existe un beneficio al evitar el análisis sintáctico repetido de las mismas sentencias.
Desventajas
Depuración difícil. MySQL no ofrece un debugger integrado para procedimientos almacenados. No puedes poner breakpoints, inspeccionar variables paso a paso ni usar herramientas de profiling como las que existen en lenguajes de programación. Cuando un procedimiento falla o produce resultados incorrectos, la depuración se reduce a añadir SELECT intermedios para imprimir valores de variables, lo cual es tedioso y poco práctico en procedimientos largos.
Portabilidad limitada. La sintaxis de los procedimientos almacenados varía significativamente entre motores de base de datos. Un procedimiento escrito para MySQL no funcionará en PostgreSQL, SQL Server ni Oracle sin modificaciones sustanciales. Si en el futuro necesitas migrar a otro motor, los procedimientos serán una de las partes más costosas de portar.
Consumo de recursos del servidor. La lógica compleja en procedimientos consume CPU y memoria del servidor de base de datos. A diferencia del código de aplicación, que se puede escalar horizontalmente añadiendo más servidores de aplicación, el servidor de base de datos suele ser un recurso más limitado y costoso de escalar. Mover demasiada lógica a procedimientos puede sobrecargar el servidor de base de datos.
Control de versiones. Los procedimientos viven en la base de datos, no en archivos de código fuente. Esto dificulta integrarlos en flujos de trabajo modernos con Git, revisiones de código (pull requests) y despliegues automatizados. Aunque se pueden exportar las definiciones a archivos SQL y versionarlos, requiere disciplina adicional que muchos equipos no mantienen.
Lógica de negocio dividida. Cuando parte de la lógica vive en la aplicación y parte en procedimientos almacenados, puede ser difícil para un desarrollador nuevo entender el flujo completo de una operación. Este "código oculto" en la base de datos a veces se convierte en una fuente de bugs difíciles de rastrear.
Cuándo usar procedimientos almacenados
Los procedimientos almacenados son especialmente útiles en estos escenarios. Si lo que necesitas es devolver un único valor calculado, las funciones almacenadas pueden ser una alternativa más adecuada:
- Operaciones atómicas que involucran varias tablas y deben ejecutarse como una unidad (crear un pedido con sus detalles, actualizar stock y registrar el movimiento). Combinarlos con transacciones garantiza consistencia.
- Acceso controlado a datos sensibles donde los usuarios no deben tener SELECT/INSERT/UPDATE directo en las tablas.
- Lógica compartida entre múltiples aplicaciones que acceden a la misma base de datos.
- Procesamiento por lotes que opera sobre grandes volúmenes de datos y se beneficia de ejecutarse directamente en el servidor.
En cambio, es mejor evitarlos cuando la lógica es simple (un SELECT con un WHERE), cuando necesitas portabilidad entre motores de base de datos, o cuando prefieres mantener toda la lógica de negocio en el código de la aplicación por facilidad de mantenimiento y testing.
Ver procedimientos existentes
Para listar todos los procedimientos almacenados de una base de datos, MySQL ofrece varias opciones. La más directa es SHOW PROCEDURE STATUS:
-- Listar procedimientos de la base de datos actual
SHOW PROCEDURE STATUS WHERE Db = 'tienda_mysql';Para ver la definición completa de un procedimiento (el código fuente con el que fue creado), se usa SHOW CREATE PROCEDURE:
-- Ver la definición de un procedimiento
SHOW CREATE PROCEDURE contar_productos\GEl formato \G muestra el resultado en formato vertical, que es más legible para definiciones largas.
Limpieza
DROP PROCEDURE IF EXISTS contar_productos;En el siguiente artículo veremos el comando DELIMITER, necesario para definir procedimientos almacenados en el cliente MySQL.
Escrito por Eduardo Lázaro
