Skip to content

MySQL - Capitulo 4

Una guía através del camino de las bases de datos con MySQL uno de los sistemas de gestión de bases de datos más conocidos y utilizados en el mundo de la programación.

Volver a Página Principal

Ver Codigo en GitLab


🚀 Módulo 4: Funciones Integradas en MySQL ✨ ¡Potencia tus Consultas!

Section titled “🚀 Módulo 4: Funciones Integradas en MySQL ✨ ¡Potencia tus Consultas!”

¡Hola de nuevo! 👋 ¡Bienvenidos al Módulo 4! En los módulos anteriores, hemos aprendido a estructurar nuestra base de datos con DDL, manipular datos con DML, realizar consultas complejas con JOIN y funciones de agregación, y hemos simplificado nuestra vida con las vistas. Ahora, vamos a sumergirnos en el fascinante mundo de las funciones integradas de MySQL.

Piensa en las funciones como pequeños “trabajadores” inteligentes 👷‍♂️ que puedes invocar en tus consultas para transformar, formatear o calcular valores, sin tener que escribir lógica compleja. Son súper útiles para hacer tus reportes más legibles, tus cálculos más precisos y tus datos más presentables. ¡Prepárate para llevar tus habilidades de consulta al siguiente nivel! 📈


Antes de avanzar, hagamos un breve recordatorio de dos conceptos fundamentales que ya hemos explorado:

  • DDL (Data Definition Language) - Lenguaje de Definición de Datos:

    • Se refiere a los comandos SQL que se utilizan para definir, modificar o eliminar la estructura de la base de datos y sus objetos. Son las instrucciones que crearon tus tablas, sus columnas, sus tipos de datos y las relaciones.
    • Ejemplos clave que ya vimos: CREATE TABLE, ALTER TABLE, DROP TABLE, CREATE VIEW, DROP VIEW. Piensa en DDL como la “arquitectura” de tu base de datos. 🏗️
  • DML (Data Manipulation Language) - Lenguaje de Manipulación de Datos:

    • Son los comandos SQL que te permiten gestionar y manipular los datos dentro de los objetos de la base de datos. Con DML, interactúas directamente con la información que se almacena.
    • Ejemplos clave que ya vimos: INSERT INTO (añadir datos), UPDATE (modificar datos), DELETE FROM (eliminar datos) y, por supuesto, SELECT (consultar datos, la más usada). DML es cómo “interactúas” con la información que reside en tu arquitectura. ✍️

Ahora que tenemos esos conceptos frescos, ¡volvamos a las funciones!


4.1. ¿Qué Son las Funciones Integradas de MySQL? 🤔

Section titled “4.1. ¿Qué Son las Funciones Integradas de MySQL? 🤔”

Una función en MySQL es una rutina predefinida que toma cero o más valores de entrada (conocidos como argumentos o parámetros), realiza una operación específica con ellos y devuelve un único valor de salida.

¿Por qué son tan importantes? 👇

  • Transformación de Datos: Cambian el formato, tipo o contenido de los datos (ej: convertir texto a mayúsculas, redondear números).
  • Cálculos: Realizan operaciones matemáticas (suma, promedio, valor absoluto).
  • Manipulación de Fechas y Horas: Extraen partes de una fecha, calculan diferencias, o formatean fechas.
  • Análisis y Resumen: (Funciones de agregación, ya las viste un poco, ¡pero las revisaremos a fondo!).
  • Consistencia: Aseguran que las operaciones complejas se realicen de la misma manera en todas tus consultas.

MySQL clasifica sus funciones en varias categorías, y las exploraremos con ejemplos relevantes para tus tablas Clientes, Ventas, Products, Ventas_Productos y Ventas_Pagos.


4.2. Funciones de Cadena (String Functions) 🧵

Section titled “4.2. Funciones de Cadena (String Functions) 🧵”

Estas funciones son tus mejores aliadas cuando trabajas con datos de texto (nombres, descripciones, etc.).

  • CONCAT(string1, string2, ...): Une dos o más cadenas de texto. Útil para combinar nombre y apellido.
    -- Ejemplo con la tabla Clients para mostrar nombre completo
    SELECT CONCAT(Nombre, ' ', Apellidos) AS NombreCompletoCliente
    FROM Clients
    WHERE Id = 'CLI-001'; -- Asume un ID de cliente existente
  • CONCAT_WS(separator, string1, string2, ...): Une cadenas de texto con un separador especificado (WS = With Separator).
    -- Si tuvieras datos separados y quisieras unirlos con un guion
    SELECT CONCAT_WS('-', '2025', '07', '29') AS FechaFormatoGuion;
    -- Resultado: '2025-07-29'
  • LENGTH(string): Devuelve la longitud de una cadena en bytes.
  • CHAR_LENGTH(string): Devuelve la longitud de una cadena en caracteres. ¡Más fiable para textos con caracteres especiales (acentos, ñ)!
    -- Longitud del nombre de un producto
    SELECT nombre_producto,
    LENGTH(nombre_producto) AS LongitudEnBytes,
    CHAR_LENGTH(nombre_producto) AS LongitudEnCaracteres
    FROM Products
    WHERE id_producto = 'PRD-001'; -- Asume un ID de producto existente
  • UPPER(string): Convierte todos los caracteres de una cadena a mayúsculas.
  • LOWER(string): Convierte todos los caracteres de una cadena a minúsculas.
    -- Normalizar nombres de clientes a mayúsculas para reportes
    SELECT UPPER(Nombre) AS NombreMayuscula, LOWER(Apellidos) AS ApellidosMinuscula
    FROM Clients
    WHERE Id = 'CLI-002';
  • SUBSTRING(string, start, length): Extrae una subcadena de una cadena.

    • start: La posición inicial (1 es el primer carácter).
    • length: La cantidad de caracteres a extraer.
    -- Extraer los primeros 3 caracteres del ID de venta para un prefijo
    SELECT id_venta, SUBSTRING(id_venta, 1, 3) AS PrefijoVenta
    FROM Ventas
    LIMIT 5; -- Muestra los primeros 5
  • TRIM(string): Elimina los espacios en blanco de ambos extremos de una cadena.
  • LTRIM(string): Elimina los espacios en blanco del lado izquierdo de una cadena.
  • RTRIM(string): Elimina los espacios en blanco del lado derecho de una cadena.
    -- Limpiar posibles espacios extra en los nombres de productos al insertarlos o reportarlos
    SELECT TRIM(' Producto A '), LTRIM(' Producto B'), RTRIM('Producto C ');
    -- Resultados: 'Producto A', 'Producto B', 'Producto C'

Estas funciones realizan operaciones matemáticas. ¡Ideales para tus montos, cantidades y precios!

  • ROUND(number, decimals): Redondea un número a un número específico de decimales.
    -- Redondear el monto total de una venta a dos decimales
    SELECT monto_total, ROUND(monto_total, 2) AS MontoRedondeado
    FROM Ventas
    WHERE id_venta = 'VTA-7015d4a451fa11f0879d06f9ed83dc74';
  • CEIL(number): Devuelve el entero más pequeño que es mayor o igual que el número (redondea hacia arriba). ⬆️
  • FLOOR(number): Devuelve el entero más grande que es menor o igual que el número (redondea hacia abajo). ⬇️
    -- Si tuvieras un cálculo de unidades parciales y quisieras redondear hacia arriba o abajo
    SELECT CEIL(10.1) AS RedondeadoArriba, FLOOR(10.9) AS RedondeadoAbajo;
    -- Resultados: 11, 10
  • ABS(number): Devuelve el valor absoluto de un número. Útil si calculas diferencias que podrían ser negativas y solo te interesa la magnitud.
    -- Calcular el valor absoluto de una posible diferencia de pago
    SELECT ABS(-50.75) AS ValorAbsoluto;
    -- Resultado: 50.75
  • MOD(number, divisor): Devuelve el resto de una división.
    -- Útil para saber si un número es par/impar, o para ciclos.
    -- Ejemplo: si cada 5 ventas se aplica una promoción
    SELECT MOD(17, 5); -- Resultado: 2

4.4. Funciones de Fecha y Hora (Date and Time Functions) 🗓️⏰

Section titled “4.4. Funciones de Fecha y Hora (Date and Time Functions) 🗓️⏰”

Cruciales para analizar tendencias de ventas, pagos y cualquier evento temporal en tu base de datos.

  • NOW(): Devuelve la fecha y hora actuales del servidor.
  • CURDATE(): Devuelve solo la fecha actual.
  • CURTIME(): Devuelve solo la hora actual.
    SELECT NOW() AS FechaYHoraActual, CURDATE() AS FechaActual, CURTIME() AS HoraActual;
    -- Resultados: '2025-07-29 21:21:51', '2025-07-29', '21:21:51' (hora de tu servidor)
  • DATE_FORMAT(date, format): Formatea una fecha/hora según una cadena de formato específica. ¡Extremadamente útil para presentar fechas de manera legible!

    • %Y: Año (4 dígitos), %m: Mes (2 dígitos), %d: Día (2 dígitos), %H: Hora (24h), %i: Minutos, %s: Segundos, %W: Día de la semana completo.
    -- Formatear la fecha de venta para un reporte de ventas
    SELECT fecha_venta, DATE_FORMAT(fecha_venta, '%d de %M de %Y') AS FechaVentaFormateada
    FROM Ventas
    WHERE id_venta = 'VTA-7015d4a451fa11f0879d06f9ed83dc74';
    -- Ejemplo de resultado: '29 de Julio de 2025'
  • DATEDIFF(date1, date2): Devuelve la diferencia en días entre dos fechas.
  • TIMEDIFF(time1, time2): Devuelve la diferencia de tiempo entre dos horas/marcas de tiempo.
    -- Calcular cuántos días han pasado desde una venta hasta hoy
    SELECT id_venta, fecha_venta, DATEDIFF(CURDATE(), fecha_venta) AS DiasDesdeVenta
    FROM Ventas
    WHERE id_venta = 'VTA-7015d4a451fa11f0879d06f9ed83dc74';
    -- Calcular la duración de una operación (si tuvieras columnas de inicio y fin)
    -- SELECT TIMEDIFF('15:00:00', '10:30:00'); -- Resultado: '04:30:00'

YEAR(), MONTH(), DAY(), HOUR(), MINUTE(), SECOND()

Section titled “YEAR(), MONTH(), DAY(), HOUR(), MINUTE(), SECOND()”
  • Extraen la parte de año, mes, día, hora, minuto o segundo de una fecha/hora.
    -- Analizar ventas por año y mes
    SELECT id_venta,
    YEAR(fecha_venta) AS AñoVenta,
    MONTH(fecha_venta) AS MesVenta,
    DAY(fecha_venta) AS DiaVenta
    FROM Ventas
    WHERE id_venta = 'VTA-7015d4a451fa11f0879d06f9ed83dc74';

4.5. Funciones de Agregación (Aggregate Functions) 📊

Section titled “4.5. Funciones de Agregación (Aggregate Functions) 📊”

Aunque ya las has visto en el Módulo 2 con GROUP BY, es fundamental recordarlas aquí porque son funciones al fin y al cabo, y operan sobre conjuntos de filas para devolver un único valor resumido.

  • COUNT(expression): Cuenta el número de filas o el número de valores no nulos en una columna.

    • COUNT(*): Cuenta todas las filas.
    • COUNT(column_name): Cuenta las filas donde column_name no es NULL.
    -- Total de clientes
    SELECT COUNT(*) AS TotalClientes FROM Clients;
    -- Número de pagos registrados para una venta específica
    SELECT COUNT(id_pago) AS NumeroDePagos
    FROM Ventas_Pagos
    WHERE id_venta = 'VTA-7015d4a451fa11f0879d06f9ed83dc74';
  • SUM(numeric_column): Calcula la suma total de una columna numérica.
    -- Suma total de todos los montos de venta
    SELECT SUM(monto_total) AS VentasTotalesGlobales FROM Ventas;
    -- Suma de pagos para una venta específica (similar a lo que ya haces en tu vista)
    SELECT SUM(monto_pago) AS TotalPagadoEnVenta
    FROM Ventas_Pagos
    WHERE id_venta = 'VTA-7015d4a451fa11f0879d06f9ed83dc74';
  • AVG(numeric_column): Calcula el promedio de los valores en una columna numérica.
    -- Precio promedio de los productos
    SELECT AVG(precio_unitario) AS PrecioPromedioProducto FROM Products;
    -- Monto promedio de las ventas
    SELECT AVG(monto_total) AS MontoPromedioVenta FROM Ventas;
  • MIN(column): Devuelve el valor mínimo de una columna.
  • MAX(column): Devuelve el valor máximo de una columna.
    -- La fecha de la primera venta y la última venta registrada
    SELECT MIN(fecha_venta) AS PrimeraVenta, MAX(fecha_venta) AS UltimaVenta FROM Ventas;
    -- El producto más barato y el más caro
    SELECT MIN(precio_unitario) AS ProductoMasBarato, MAX(precio_unitario) AS ProductoMasCaro FROM Products;

4.6. Uso Combinado de Funciones y Cláusulas 🤝

Section titled “4.6. Uso Combinado de Funciones y Cláusulas 🤝”

Las funciones son increíblemente poderosas cuando se combinan con las cláusulas WHERE, GROUP BY, HAVING y ORDER BY.

Ejemplo 1: Clientes con nombre que empieza por ‘J’ y fecha de última compra

Section titled “Ejemplo 1: Clientes con nombre que empieza por ‘J’ y fecha de última compra”
SELECT
Id,
CONCAT(Nombre, ' ', Apellidos) AS NombreCompleto,
MAX(fecha_venta) AS UltimaCompra
FROM
Clients C
JOIN
Ventas V ON C.Id = V.id_cliente
WHERE
LEFT(C.Nombre, 1) = 'J' -- Usamos LEFT() para filtrar por la primera letra
GROUP BY
Id, Nombre, Apellidos
ORDER BY
UltimaCompra DESC;

Ejemplo 2: Ventas del mes actual, formateadas y con total de deuda

Section titled “Ejemplo 2: Ventas del mes actual, formateadas y con total de deuda”
SELECT
V.id_venta,
DATE_FORMAT(V.fecha_venta, '%Y-%m-%d') AS FechaVenta,
CONCAT(C.Nombre, ' ', C.Apellidos) AS Cliente,
V.monto_total,
SUM(COALESCE(PP.monto_pago, 0)) AS TotalPagado,
(V.monto_total - SUM(COALESCE(PP.monto_pago, 0))) AS DeudaPendiente
FROM
Ventas V
JOIN
Clients C ON C.Id = V.id_cliente
LEFT JOIN
Ventas_Pagos PP ON V.id_venta = PP.id_venta
WHERE
YEAR(V.fecha_venta) = YEAR(CURDATE()) AND MONTH(V.fecha_venta) = MONTH(CURDATE()) -- Filtra por el año y mes actual
GROUP BY
V.id_venta, V.fecha_venta, C.Nombre, C.Apellidos, V.monto_total
HAVING
DeudaPendiente > 0 -- Solo ventas con deuda
ORDER BY
DeudaPendiente DESC;

4.7. Funciones Almacenadas (Stored Functions) 🧠

Section titled “4.7. Funciones Almacenadas (Stored Functions) 🧠”

¡Aquí viene la parte avanzada y poderosa! A diferencia de las funciones integradas de MySQL (como CONCAT o SUM), las funciones almacenadas son programas que tú creas y almacenas directamente en la base de datos. Se parecen a las funciones en lenguajes de programación: toman parámetros, realizan operaciones complejas y devuelven un único valor.

¿Por qué crear funciones almacenadas? 🤔

  • Reutilización de Código: Escribes la lógica una vez y la usas en múltiples consultas, vistas o incluso en otras funciones almacenadas o procedimientos almacenados. 🔄
  • Lógica de Negocio Compleja: Encapsulan reglas de negocio complejas que no se pueden resolver fácilmente con una sola consulta SQL.
  • Rendimiento: Pueden ser más rápidas que ejecutar la misma lógica repetidamente en el cliente, ya que se compilan y residen en el servidor. ⚡
  • Seguridad: Puedes conceder permisos para ejecutar una función sin dar acceso directo a las tablas subyacentes. 🔒

Sintaxis Básica para Crear una Función Almacenada

Section titled “Sintaxis Básica para Crear una Función Almacenada”
DELIMITER // -- Cambiamos el delimitador temporalmente
CREATE FUNCTION nombre_funcion (param1 TIPO_DATO, param2 TIPO_DATO, ...)
RETURNS TIPO_DATO_RETORNO -- Tipo de dato que la función devolverá
DETERMINISTIC -- Opcional, indica que para la misma entrada, siempre devuelve la misma salida
BEGIN
-- Declaración de variables locales (opcional)
DECLARE variable_local TIPO_DATO;
-- Lógica SQL y de programación (SELECT, IF, WHILE, SET, etc.)
-- Puedes usar SELECT INTO para guardar resultados de consultas en variables
-- La función DEBE devolver un valor
RETURN valor_a_retornar;
END //
DELIMITER ; -- Volvemos el delimitador a su valor por defecto
  • DELIMITER //: Antes de crear funciones o procedimientos almacenados, es común cambiar el delimitador. Esto se debe a que el cuerpo de la función puede contener sentencias SQL que terminan con ;, y no queremos que MySQL interprete esos puntos y coma como el fin de la declaración CREATE FUNCTION. Después de crearla, volvemos a DELIMITER ;.

    ¡Importante sobre el DELIMITER! ⚠️ Aunque // es un delimitador muy común y fácil de usar, no es el único que puedes o debes usar. Puedes elegir casi cualquier secuencia de caracteres que no aparezca dentro del cuerpo de tu función o procedimiento almacenado.

    Ejemplos de otros delimitadores válidos:

    • $$
    • ###
    • @@
    • @_FIN_@ (¡Puedes usar secuencias más complejas!)

    Lo crucial es que el delimitador que elijas sea una secuencia de caracteres que no se utilice internamente dentro de las sentencias SQL que componen el cuerpo de tu función o procedimiento. Una vez que la función ha sido creada, MySQL “olvida” el delimitador temporal y puedes volver a usar ; normalmente para tus consultas.

  • nombre_funcion: Un nombre significativo para tu función.

  • Parámetros: Entre paréntesis, defines los parámetros de entrada y sus tipos de datos.

  • RETURNS TIPO_DATO_RETORNO: Especifica el tipo de dato que la función devolverá. ¡Una función siempre devuelve un valor!

  • BEGIN...END: Contiene la lógica de la función.

  • RETURN valor;: Sentencia obligatoria para devolver el resultado.

Ejemplo Práctico: Función para Calcular la Deuda Pendiente de una Venta 💰

Section titled “Ejemplo Práctico: Función para Calcular la Deuda Pendiente de una Venta 💰”

¡Vamos a crear una función que encapsule la lógica de cálculo de la deuda, que ya usas en tu vista Vista_Estado_Cuentas_Por_Cobrar!

DELIMITER //
CREATE FUNCTION CalcularDeudaVenta (p_id_venta VARCHAR(255))
RETURNS DECIMAL(10, 2) -- Asumiendo que los montos son DECIMAL
READS SQL DATA -- Indica que la función lee datos, pero no los modifica
BEGIN
DECLARE total_venta_val DECIMAL(10, 2);
DECLARE total_pagado_val DECIMAL(10, 2);
DECLARE deuda DECIMAL(10, 2);
-- Obtener el monto total de la venta
SELECT monto_total INTO total_venta_val
FROM Ventas
WHERE id_venta = p_id_venta;
-- Obtener la suma de todos los pagos para esa venta
SELECT SUM(COALESCE(monto_pago, 0)) INTO total_pagado_val
FROM Ventas_Pagos
WHERE id_venta = p_id_venta;
-- Calcular la deuda
SET deuda = total_venta_val - COALESCE(total_pagado_val, 0);
RETURN deuda;
END //
DELIMITER ;

4.8. Control de Flujo: Condicionales y Bucles en Funciones Almacenadas 🚦🔁

Section titled “4.8. Control de Flujo: Condicionales y Bucles en Funciones Almacenadas 🚦🔁”

Las funciones almacenadas no solo ejecutan SQL, ¡también pueden tener lógica de programación! Esto incluye sentencias condicionales y bucles.

Condicionales (IF...THEN...ELSEIF...ELSE...END IF)

Section titled “Condicionales (IF...THEN...ELSEIF...ELSE...END IF)”

Las estructuras condicionales te permiten ejecutar diferentes bloques de código según si una condición es verdadera o falsa. Son fundamentales para la toma de decisiones dentro de tu lógica.

DELIMITER //
CREATE FUNCTION DeterminarEstadoDeuda (p_id_venta VARCHAR(255))
RETURNS VARCHAR(50)
READS SQL DATA
BEGIN
DECLARE deuda DECIMAL(10, 2);
DECLARE estado VARCHAR(50);
-- Reutilizamos nuestra función anterior para calcular la deuda ✨
SET deuda = CalcularDeudaVenta(p_id_venta);
IF deuda > 0 THEN
SET estado = 'PENDIENTE';
ELSEIF deuda = 0 THEN
SET estado = 'PAGADA';
ELSE
-- Esto podría indicar un error, un pago en exceso o una nota de crédito
SET estado = 'SOBREPAGADO / ERROR';
END IF;
RETURN estado;
END //
DELIMITER ;
-- Ejemplo de uso:
SELECT
id_venta,
monto_total,
CalcularDeudaVenta(id_venta) AS Deuda,
DeterminarEstadoDeuda(id_venta) AS Estado
FROM
Ventas
LIMIT 5;

Bucles (WHILE...DO...END WHILE, LOOP...END LOOP y REPEAT...UNTIL...END REPEAT)

Section titled “Bucles (WHILE...DO...END WHILE, LOOP...END LOOP y REPEAT...UNTIL...END REPEAT)”

Los bucles te permiten repetir un bloque de código varias veces. Aunque muchas operaciones en SQL se resuelven de forma más eficiente con sentencias declarativas (SELECT con JOINs, GROUP BY, etc.), los bucles son útiles para lógica secuencial que requiere iteración explícita, a menudo cuando se procesan datos fila por fila usando cursores.

WHILE...DO...END WHILE: Repite un bloque de código mientras una condición sea verdadera. La condición se evalúa al inicio de cada iteración.

DELIMITER //
CREATE FUNCTION CalcularInteresSimple(p_principal DECIMAL(10,2), p_tasa DECIMAL(5,2), p_periodos INT)
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
DECLARE monto_final DECIMAL(10,2) DEFAULT p_principal;
DECLARE i INT DEFAULT 1;
WHILE i <= p_periodos DO
SET monto_final = monto_final * (1 + p_tasa); -- Aplicamos la tasa
SET i = i + 1;
END WHILE;
RETURN ROUND(monto_final, 2); -- Redondeamos para un mejor formato
END //
DELIMITER ;
-- Ejemplo de uso:
-- Calcular interés simple de $1000 al 5% por 3 periodos
SELECT CalcularInteresSimple(1000.00, 0.05, 3); -- Resultado: ~1157.63

LOOP...END LOOP con LEAVE: Un bucle infinito que debe terminarse explícitamente con LEAVE (similar a break en otros lenguajes) o ITERATE (similar a continue). Se usa a menudo con cursores.

DELIMITER //
-- Función para buscar el ID de un producto cuyo nombre empiece con un prefijo
-- y devolver el primer match encontrado.
CREATE FUNCTION ObtenerPrimerProductoPorPrefijo(p_prefijo VARCHAR(50))
RETURNS VARCHAR(255)
READS SQL DATA
BEGIN
DECLARE v_producto_nombre VARCHAR(255);
DECLARE done INT DEFAULT FALSE;
-- Declaramos un cursor para iterar sobre los productos
DECLARE cur_productos CURSOR FOR
SELECT nombre_producto FROM Products WHERE nombre_producto LIKE CONCAT(p_prefijo, '%');
-- Declaramos un manejador para la condición NOT FOUND (cuando el cursor se queda sin filas)
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur_productos; -- Abrimos el cursor
read_loop: LOOP -- Inicio del bucle infinito
FETCH cur_productos INTO v_producto_nombre; -- Leemos la siguiente fila del cursor
IF done THEN
LEAVE read_loop; -- Si no hay más filas, salimos del bucle
END IF;
-- Aquí podrías añadir más lógica
-- Para este ejemplo, solo queremos el primer match, así que salimos.
RETURN v_producto_nombre; -- Devolvemos el nombre y salimos
END LOOP;
CLOSE cur_productos; -- Cerramos el cursor
RETURN NULL; -- Si no se encontró ningún producto, devolvemos NULL
END //
DELIMITER ;
-- Ejemplo de uso:
SELECT ObtenerPrimerProductoPorPrefijo('Lap'); -- Devolverá el primer producto que empiece con 'Lap'
SELECT ObtenerPrimerProductoPorPrefijo('XYZ'); -- Devolverá NULL si no hay coincidencias

REPEAT...UNTIL...END REPEAT: Repite un bloque de código hasta que una condición sea verdadera. La condición se evalúa al final de cada iteración, garantizando que el bloque se ejecute al menos una vez.

-- Ejemplo: Calcular la suma de los números hasta un límite, similar a WHILE pero con condición al final
DELIMITER //
CREATE FUNCTION SumarHasta(p_limite INT)
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE i INT DEFAULT 0;
DECLARE suma INT DEFAULT 0;
REPEAT
SET suma = suma + i;
SET i = i + 1;
UNTIL i > p_limite END REPEAT;
RETURN suma;
END //
DELIMITER ;
-- Ejemplo de uso:
SELECT SumarHasta(5); -- Resultado: 15 (0+1+2+3+4+5)

4.9. Manejo de Errores y Validadores (DECLARE HANDLER) 🚨

Section titled “4.9. Manejo de Errores y Validadores (DECLARE HANDLER) 🚨”

MySQL no tiene un bloque TRY-CATCH como otros lenguajes (Java, C#, Python). En su lugar, utiliza manejadores de condiciones (Condition Handlers) para controlar errores y advertencias en el código de las funciones (y procedimientos) almacenadas.

Un manejador te permite especificar una acción a tomar cuando ocurre una condición particular (como una excepción SQL, una advertencia o NOT FOUND).

DECLARE [CONTINUE | EXIT | UNDO] HANDLER
FOR [SQLSTATE 'codigo_sqlstate' | CONDICION | SQLWARNING | NOT FOUND | SQLEXCEPTION]
accion;
  • CONTINUE: El manejador se ejecuta y luego el control del flujo vuelve a la sentencia que causó el error (o la siguiente si la sentencia no se puede completar). El resto del bloque BEGIN...END continúa ejecutándose.

  • EXIT: El manejador se ejecuta y luego el control del flujo sale inmediatamente del bloque BEGIN...END actual. Es lo más parecido a un throw/catch que sale de la función.

  • UNDO: No implementado en MySQL, pero existe en el estándar SQL.

  • FOR: Define la condición que activará el manejador:

    • SQLSTATE 'código': Para un código SQLSTATE específico (ej: '23000' para error de duplicado de entrada).
    • CONDICION: Un nombre de condición previamente declarado (ej: DECLARE nombre_condicion CONDITION FOR SQLSTATE '23000').
    • SQLWARNING: Para cualquier advertencia (código SQLSTATE que empieza con ‘01’).
    • NOT FOUND: Cuando un SELECT INTO o FETCH de un cursor no encuentra filas (código SQLSTATE '02000'). ¡Muy común con cursores!
    • SQLEXCEPTION: Para cualquier error SQL (código SQLSTATE que no empieza con ‘00’ o ‘01’). ¡Similar a un catch genérico!
  • accion: La acción a realizar cuando se activa el manejador. Puede ser una o más sentencias SQL.

Ejemplo Práctico: Manejo de Errores al No Encontrar Datos 🚫

Section titled “Ejemplo Práctico: Manejo de Errores al No Encontrar Datos 🚫”

Vamos a modificar nuestra función CalcularDeudaVenta para que maneje el caso en que p_id_venta no exista.

DELIMITER //
CREATE FUNCTION CalcularDeudaVentaSegura (p_id_venta VARCHAR(255))
RETURNS DECIMAL(10, 2)
READS SQL DATA
BEGIN
DECLARE total_venta_val DECIMAL(10, 2) DEFAULT 0.00;
DECLARE total_pagado_val DECIMAL(10, 2) DEFAULT 0.00;
DECLARE deuda DECIMAL(10, 2) DEFAULT 0.00;
DECLARE venta_encontrada INT DEFAULT 0; -- Bandera para saber si la venta existe
-- DECLARE HANDLER para NOT FOUND (cuando el SELECT INTO no encuentre filas)
DECLARE CONTINUE HANDLER FOR NOT FOUND SET venta_encontrada = 0;
-- Intentar obtener el monto total de la venta
SELECT monto_total INTO total_venta_val
FROM Ventas
WHERE id_venta = p_id_venta;
-- Si la venta no se encontró, retornamos un valor especial (ej: -1 o NULL)
IF venta_encontrada = 0 THEN
RETURN -1.00; -- Indicador de Venta no encontrada
END IF;
-- Reiniciamos la bandera para el siguiente SELECT INTO
SET venta_encontrada = 1; -- Asumimos que sí hay pagos o que la suma será 0
-- Obtener la suma de todos los pagos para esa venta
-- COALESCE(SUM(...), 0) ya maneja el caso de no pagos como 0
SELECT SUM(monto_pago) INTO total_pagado_val
FROM Ventas_Pagos
WHERE id_venta = p_id_venta;
-- Calcular la deuda
SET deuda = total_venta_val - COALESCE(total_pagado_val, 0);
RETURN deuda;
END //
DELIMITER ;
-- Ejemplo de uso:
SELECT
id_venta,
CalcularDeudaVentaSegura(id_venta) AS DeudaConManejoError
FROM
Ventas
LIMIT 5;
-- Prueba con un ID de venta que no existe:
SELECT CalcularDeudaVentaSegura('VTA-ID-NO-EXISTE'); -- Devolverá -1.00

Este ejemplo muestra cómo puedes detectar y manejar la ausencia de datos, lo que hace que tus funciones sean más robustas y amigables.


Para convertirte en un maestro de las funciones en MySQL, consulta siempre la fuente oficial y recursos confiables:


¡Es hora de poner en práctica estas poderosas herramientas!

  1. Funciones de Cadena y Numéricas:

    • Obtén el nombre completo de todos los clientes en mayúsculas y la longitud de su nombre (CHAR_LENGTH).
    • Calcula el precio promedio de los productos, redondeado a cero decimales.
  2. Funciones de Fecha y Hora:

    • Crea una consulta que muestre todas las ventas realizadas en el último mes, formateando la fecha_venta como “Día, DD de Mes de YYYY” (ej: “Martes, 29 de Julio de 2025”). 🗓️
    • Para cada venta, calcula la cantidad de días transcurridos desde la fecha de venta hasta hoy.
  3. Funciones de Agregación (Repaso y Combinación):

    • Para cada cliente, calcula el monto total gastado y el número de ventas que ha realizado. Muestra solo aquellos clientes que hayan gastado más de $500 y hayan realizado al menos 2 ventas.
    • Identifica el producto más vendido (por cantidad) y el producto que generó más ingresos (por cantidad * precio_venta).
  4. Función Almacenada con Condicionales y Manejo de Errores:

    • Implementa la función CalcularDeudaVentaSegura que se explicó en este módulo, la cual devuelve -1.00 si la venta no se encuentra.
    • Crea otra función ObtenerTipoCliente(p_id_cliente VARCHAR(255)) que, basándose en el TOTAL_GASTADO del cliente (puedes calcularlo dentro de la función o pasar un parámetro para simplificar), devuelva:
      • 'VIP' si el cliente ha gastado más de $1000.
      • 'REGULAR' si ha gastado entre $100 y $1000 (inclusive).
      • 'NUEVO' si ha gastado menos de $100.
      • 'NO ENCONTRADO' si el ID del cliente no existe (¡usa DECLARE HANDLER aquí!).
    • Usa tus nuevas funciones en una consulta que muestre ID_CLIENTE, NOMBRE_CLIENTE, TOTAL_GASTADO y el TIPO_CLIENTE para todos los clientes.
  5. Reflexiona:

    • ¿Cómo cambian tus consultas al usar funciones integradas? ¿Las hacen más legibles o más complejas?
    • ¿En qué escenarios específicos ves el mayor potencial para usar funciones almacenadas con lógica de control de flujo y manejo de errores en tu trabajo con la base de datos?

¡Este módulo te ha dado las herramientas para manipular y analizar tus datos con una flexibilidad increíble! ¡Sigue experimentando y verás cómo tus consultas se vuelven más potentes y tus reportes más perspicaces! 💪 ¡A codificar! 🚀