Variables

Los procedimientos almacenados pueden usar variables locales declaradas con DECLARE y variables de sesión con el prefijo @. Las variables locales solo existen dentro del bloque BEGIN...END donde se declaran.

Entender cómo funcionan las variables es fundamental para escribir procedimientos que hagan algo más que ejecutar consultas simples. Las variables te permiten almacenar valores intermedios, construir lógica condicional, acumular resultados y controlar el flujo de ejecución. Son el pegamento que une las diferentes sentencias SQL dentro de un procedimiento en una secuencia lógica coherente.

Variables locales con DECLARE

Las variables locales se crean con la sentencia DECLARE y existen únicamente durante la ejecución del procedimiento. Cuando el procedimiento termina, las variables desaparecen sin dejar rastro. No afectan a la sesión del usuario ni a otros procedimientos.

DELIMITER //
 
CREATE PROCEDURE ejemplo_variables()
BEGIN
    -- Declarar variables (siempre al inicio del bloque BEGIN)
    DECLARE nombre VARCHAR(100);
    DECLARE precio DECIMAL(10,2) DEFAULT 0.00;
    DECLARE cantidad INT DEFAULT 1;
    DECLARE activo BOOLEAN DEFAULT TRUE;
 
    -- Asignar valores con SET
    SET nombre = 'Producto de prueba';
    SET precio = 99.99;
 
    SELECT nombre, precio, cantidad, activo;
END //
 
DELIMITER ;
CALL ejemplo_variables();
nombrepreciocantidadactivo
Producto de prueba99.9911

Cada variable necesita un tipo de dato MySQL (VARCHAR, INT, DECIMAL, BOOLEAN, DATE, etc.) y opcionalmente un valor por defecto con DEFAULT. Si no especificas DEFAULT, la variable se inicializa como NULL, lo que puede causar problemas si la usas en operaciones aritméticas o concatenaciones sin verificar primero su valor.

Reglas de DECLARE

La posición de las declaraciones dentro del bloque BEGIN...END es estricta. MySQL exige que todas las sentencias DECLARE vayan al principio del bloque, antes de cualquier sentencia ejecutable (SET, SELECT, IF, etc.). Si intentas declarar una variable después de una sentencia ejecutable, obtendrás un error de sintaxis.

DELIMITER //
 
CREATE PROCEDURE ejemplo_declare()
BEGIN
    -- Múltiples variables del mismo tipo
    DECLARE x, y, z INT DEFAULT 0;
 
    SET x = 10;
    SET y = 20;
    SET z = x + y;
 
    SELECT x, y, z;
END //
 
DELIMITER ;
CALL ejemplo_declare();
xyz
102030

Puedes declarar múltiples variables del mismo tipo en una sola línea separándolas por comas, como DECLARE x, y, z INT DEFAULT 0. Todas compartirán el mismo tipo y valor por defecto. Esto es útil para declarar contadores o flags relacionados, pero si las variables tienen propósitos diferentes, es más legible declararlas por separado con nombres descriptivos.

Hay tres reglas clave que conviene recordar:

  1. DECLARE debe ir al inicio del bloque BEGIN...END, antes de cualquier otra sentencia.
  2. Puedes declarar múltiples variables del mismo tipo en una línea.
  3. El valor por defecto es NULL si no se especifica DEFAULT.

Asignar valores con SET

SET es la forma más directa de asignar un valor a una variable. Puedes asignar literales, resultados de expresiones o valores de otras variables:

SET nombre = 'Producto de prueba';
SET precio = 99.99;
SET total = precio * cantidad;
SET activo = NOT activo;

También puedes asignar múltiples variables en un solo SET separándolas por comas: SET x = 10, y = 20, z = 30. Esto es más conciso pero puede ser menos legible si las asignaciones son complejas.

Asignar valores con SELECT INTO

SELECT INTO permite asignar el resultado de una consulta a variables. Es la forma estándar de cargar datos de las tablas en variables del procedimiento:

DELIMITER //
 
CREATE PROCEDURE info_producto(IN prod_id INT)
BEGIN
    DECLARE v_nombre VARCHAR(100);
    DECLARE v_precio DECIMAL(10,2);
    DECLARE v_stock INT;
 
    SELECT nombre, precio, stock
    INTO v_nombre, v_precio, v_stock
    FROM productos
    WHERE id = prod_id;
 
    SELECT v_nombre AS producto,
           v_precio AS precio,
           v_stock AS stock,
           v_precio * v_stock AS valor_inventario;
END //
 
DELIMITER ;
CALL info_producto(1);
productopreciostockvalor_inventario
iPhone 15 Pro1299.994558499.55

Hay un requisito importante: la consulta del SELECT INTO debe devolver exactamente una fila. Si devuelve cero filas, las variables quedarán como NULL (o se activará un handler NOT FOUND si lo tienes configurado). Si devuelve más de una fila, MySQL genera un error. Por eso es habitual usar SELECT INTO con consultas que filtran por clave primaria (WHERE id = ...) o con funciones de agregación (COUNT, MAX, SUM) que siempre devuelven una sola fila.

Variables de sesión

Las variables de sesión son un mecanismo completamente diferente a las variables locales. Se crean con el prefijo @ y persisten durante toda la conexión del usuario con MySQL. No necesitan declaración previa y se pueden usar tanto dentro como fuera de los procedimientos:

-- Crear y asignar
SET @contador = 0;
SET @nombre = 'MySQL';
 
-- Usar en consultas
SELECT @contador, @nombre;
@contador@nombre
0MySQL
-- Asignar desde una consulta
SELECT @max_precio := MAX(precio) FROM productos;
SELECT @max_precio;
@max_precio
1899.99

Las variables de sesión son convenientes porque no requieren declaración y sobreviven entre sentencias, pero tienen desventajas: no tienen tipo de dato definido (MySQL lo infiere del valor asignado), persisten hasta que la conexión se cierra (lo que puede causar bugs si olvidas reinicializarlas), y no se pueden usar en cláusulas WHERE de forma fiable en todas las versiones de MySQL.

Dentro de los procedimientos, la recomendación es usar variables locales (DECLARE) para la lógica interna y reservar las variables de sesión para comunicarse con el código externo (parámetros OUT, resultados para uso posterior).

Variables locales vs variables de sesión

CaracterísticaVariable localVariable de sesión
PrefijoSin prefijo@
DeclaraciónRequiere DECLARENo requiere
Tipo de datoDefinido explícitamenteInferido del valor
ÁmbitoBloque BEGIN...ENDToda la sesión
PersistenciaSolo durante la ejecuciónHasta cerrar conexión
Uso fuera del procedimientoNo

En la práctica, esta distinción importa porque determina cuándo se limpia cada variable. Las variables locales se destruyen automáticamente al terminar el procedimiento, por lo que no hay riesgo de que valores residuales afecten a futuras ejecuciones. Las variables de sesión, en cambio, mantienen su último valor, lo que puede provocar resultados inesperados si un procedimiento asume que una variable de sesión tiene un valor determinado.

Ámbito de variables

El ámbito de una variable local está determinado por el bloque BEGIN...END donde se declara. Un bloque interior puede acceder a las variables del bloque exterior, pero no al revés:

DELIMITER //
 
CREATE PROCEDURE ejemplo_ambito()
BEGIN
    DECLARE x INT DEFAULT 10;
 
    BEGIN
        -- Este bloque interno puede acceder a x
        DECLARE y INT DEFAULT 20;
        SELECT x + y AS suma_interna;
    END;
 
    -- x sigue visible, pero y no existe aquí
    SELECT x AS valor_x;
    -- SELECT y; -- Error: Unknown column 'y'
END //
 
DELIMITER ;

Este comportamiento es idéntico al ámbito de variables en lenguajes como JavaScript (con let), Java o C. Las variables del bloque exterior son "heredadas" por los bloques interiores, pero las variables internas no se filtran hacia fuera.

Los bloques anidados son especialmente útiles cuando trabajas con múltiples cursores, ya que cada bloque puede tener su propio handler NOT FOUND sin que interfiera con los cursores de otros bloques.

Ejemplo práctico: calcular descuento

Este ejemplo combina variables locales, SELECT INTO y condicionales para implementar una lógica de descuentos escalonados. Demuestra cómo las variables sirven de puente entre las consultas a la base de datos y la lógica de negocio:

DELIMITER //
 
CREATE PROCEDURE calcular_descuento(IN prod_id INT)
BEGIN
    DECLARE v_nombre VARCHAR(100);
    DECLARE v_precio DECIMAL(10,2);
    DECLARE v_descuento DECIMAL(5,2) DEFAULT 0;
    DECLARE v_precio_final DECIMAL(10,2);
 
    SELECT nombre, precio INTO v_nombre, v_precio
    FROM productos WHERE id = prod_id;
 
    -- Calcular descuento según precio
    IF v_precio > 1000 THEN
        SET v_descuento = 15.00;
    ELSEIF v_precio > 500 THEN
        SET v_descuento = 10.00;
    ELSEIF v_precio > 100 THEN
        SET v_descuento = 5.00;
    END IF;
 
    SET v_precio_final = v_precio - (v_precio * v_descuento / 100);
 
    SELECT v_nombre AS producto,
           v_precio AS precio_original,
           v_descuento AS descuento_pct,
           v_precio_final AS precio_final;
END //
 
DELIMITER ;
CALL calcular_descuento(1);
productoprecio_originaldescuento_pctprecio_final
iPhone 15 Pro1299.9915.001104.99
CALL calcular_descuento(22);
productoprecio_originaldescuento_pctprecio_final
Camiseta algodón básica19.990.0019.99

El patrón es claro: primero se cargan los datos de la tabla en variables locales con SELECT INTO, luego se aplica la lógica de negocio manipulando esas variables con SET e IF, y finalmente se devuelve el resultado con un SELECT que referencia las variables calculadas.

Limpieza

DROP PROCEDURE IF EXISTS ejemplo_variables;
DROP PROCEDURE IF EXISTS ejemplo_declare;
DROP PROCEDURE IF EXISTS info_producto;
DROP PROCEDURE IF EXISTS ejemplo_ambito;
DROP PROCEDURE IF EXISTS calcular_descuento;

En el siguiente artículo veremos la sentencia IF para control de flujo dentro de procedimientos.

Escrito por Eduardo Lázaro