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.
Síguenos con el tutorial
Section titled “Síguenos con el tutorial”- En QuéPasalinux MySQL - Funciones Integradas en MySQL un vídeo práctico sobre MySQL y Docker
Volver a Página Principal
Ver Codigo en GitLab
📚 Curso de Capacitación de MySQL
Section titled “📚 Curso de Capacitación de MySQL”🚀 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! 📈
¿Recordamos DDL y DML? 🤔
Section titled “¿Recordamos DDL y DML? 🤔”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()
y CONCAT_WS()
Section titled “CONCAT() y CONCAT_WS()”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 completoSELECT CONCAT(Nombre, ' ', Apellidos) AS NombreCompletoClienteFROM ClientsWHERE Id = 'CLI-001'; -- Asume un ID de cliente existenteCONCAT_WS(separator, string1, string2, ...)
: Une cadenas de texto con un separador especificado (WS = With Separator).-- Si tuvieras datos separados y quisieras unirlos con un guionSELECT CONCAT_WS('-', '2025', '07', '29') AS FechaFormatoGuion;-- Resultado: '2025-07-29'
LENGTH()
/ CHAR_LENGTH()
Section titled “LENGTH() / CHAR_LENGTH()”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 productoSELECT nombre_producto,LENGTH(nombre_producto) AS LongitudEnBytes,CHAR_LENGTH(nombre_producto) AS LongitudEnCaracteresFROM ProductsWHERE id_producto = 'PRD-001'; -- Asume un ID de producto existente
UPPER()
y LOWER()
Section titled “UPPER() y LOWER()”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 reportesSELECT UPPER(Nombre) AS NombreMayuscula, LOWER(Apellidos) AS ApellidosMinusculaFROM ClientsWHERE Id = 'CLI-002';
SUBSTRING()
/ SUBSTR()
Section titled “SUBSTRING() / SUBSTR()”-
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 prefijoSELECT id_venta, SUBSTRING(id_venta, 1, 3) AS PrefijoVentaFROM VentasLIMIT 5; -- Muestra los primeros 5
TRIM()
, LTRIM()
, RTRIM()
Section titled “TRIM(), LTRIM(), RTRIM()”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 reportarlosSELECT TRIM(' Producto A '), LTRIM(' Producto B'), RTRIM('Producto C ');-- Resultados: 'Producto A', 'Producto B', 'Producto C'
4.3. Funciones Numéricas 🔢
Section titled “4.3. Funciones Numéricas 🔢”Estas funciones realizan operaciones matemáticas. ¡Ideales para tus montos, cantidades y precios!
ROUND()
Section titled “ROUND()”ROUND(number, decimals)
: Redondea un número a un número específico de decimales.-- Redondear el monto total de una venta a dos decimalesSELECT monto_total, ROUND(monto_total, 2) AS MontoRedondeadoFROM VentasWHERE id_venta = 'VTA-7015d4a451fa11f0879d06f9ed83dc74';
CEIL()
/ FLOOR()
Section titled “CEIL() / FLOOR()”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 abajoSELECT 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 pagoSELECT 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ónSELECT 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()
/ CURDATE()
/ CURTIME()
Section titled “NOW() / CURDATE() / CURTIME()”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()
Section titled “DATE_FORMAT()”-
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 ventasSELECT fecha_venta, DATE_FORMAT(fecha_venta, '%d de %M de %Y') AS FechaVentaFormateadaFROM VentasWHERE id_venta = 'VTA-7015d4a451fa11f0879d06f9ed83dc74';-- Ejemplo de resultado: '29 de Julio de 2025'
DATEDIFF()
/ TIMEDIFF()
Section titled “DATEDIFF() / TIMEDIFF()”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 hoySELECT id_venta, fecha_venta, DATEDIFF(CURDATE(), fecha_venta) AS DiasDesdeVentaFROM VentasWHERE 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 mesSELECT id_venta,YEAR(fecha_venta) AS AñoVenta,MONTH(fecha_venta) AS MesVenta,DAY(fecha_venta) AS DiaVentaFROM VentasWHERE 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()
Section titled “COUNT()”-
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 dondecolumn_name
no esNULL
.
-- Total de clientesSELECT COUNT(*) AS TotalClientes FROM Clients;-- Número de pagos registrados para una venta específicaSELECT COUNT(id_pago) AS NumeroDePagosFROM Ventas_PagosWHERE id_venta = 'VTA-7015d4a451fa11f0879d06f9ed83dc74';
SUM(numeric_column)
: Calcula la suma total de una columna numérica.-- Suma total de todos los montos de ventaSELECT 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 TotalPagadoEnVentaFROM Ventas_PagosWHERE id_venta = 'VTA-7015d4a451fa11f0879d06f9ed83dc74';
AVG(numeric_column)
: Calcula el promedio de los valores en una columna numérica.-- Precio promedio de los productosSELECT AVG(precio_unitario) AS PrecioPromedioProducto FROM Products;-- Monto promedio de las ventasSELECT AVG(monto_total) AS MontoPromedioVenta FROM Ventas;
MIN()
/ MAX()
Section titled “MIN() / MAX()”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 registradaSELECT MIN(fecha_venta) AS PrimeraVenta, MAX(fecha_venta) AS UltimaVenta FROM Ventas;-- El producto más barato y el más caroSELECT 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 UltimaCompraFROM Clients CJOIN Ventas V ON C.Id = V.id_clienteWHERE LEFT(C.Nombre, 1) = 'J' -- Usamos LEFT() para filtrar por la primera letraGROUP BY Id, Nombre, ApellidosORDER 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 DeudaPendienteFROM Ventas VJOIN Clients C ON C.Id = V.id_clienteLEFT JOIN Ventas_Pagos PP ON V.id_venta = PP.id_ventaWHERE YEAR(V.fecha_venta) = YEAR(CURDATE()) AND MONTH(V.fecha_venta) = MONTH(CURDATE()) -- Filtra por el año y mes actualGROUP BY V.id_venta, V.fecha_venta, C.Nombre, C.Apellidos, V.monto_totalHAVING DeudaPendiente > 0 -- Solo ventas con deudaORDER 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 salidaBEGIN -- 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ónCREATE FUNCTION
. Después de crearla, volvemos aDELIMITER ;
.¡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 DECIMALREADS SQL DATA -- Indica que la función lee datos, pero no los modificaBEGIN 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 DATABEGIN 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 EstadoFROM VentasLIMIT 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 JOIN
s, 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)DETERMINISTICBEGIN 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 formatoEND //
DELIMITER ;
-- Ejemplo de uso:-- Calcular interés simple de $1000 al 5% por 3 periodosSELECT 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 DATABEGIN 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 NULLEND //
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 finalDELIMITER //
CREATE FUNCTION SumarHasta(p_limite INT)RETURNS INTDETERMINISTICBEGIN 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
).
Sintaxis Básica de DECLARE HANDLER
Section titled “Sintaxis Básica de DECLARE HANDLER”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 bloqueBEGIN...END
continúa ejecutándose. -
EXIT
: El manejador se ejecuta y luego el control del flujo sale inmediatamente del bloqueBEGIN...END
actual. Es lo más parecido a unthrow
/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 unSELECT INTO
oFETCH
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 uncatch
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 DATABEGIN 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 DeudaConManejoErrorFROM VentasLIMIT 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.
📚 Referencias y Recursos Adicionales
Section titled “📚 Referencias y Recursos Adicionales”Para convertirte en un maestro de las funciones en MySQL, consulta siempre la fuente oficial y recursos confiables:
- Documentación Oficial de MySQL:
- String Functions: ¡Un listado completo de todas las funciones de cadena!
- Numeric Functions: Todas las funciones matemáticas.
- Date and Time Functions: Para manipular fechas y horas como un pro.
- Aggregate (Group) Functions: Revisa a fondo las funciones de agregación.
- CREATE FUNCTION Statement: La guía definitiva para crear tus propias funciones almacenadas.
- DELIMITER Command: Entiende por qué cambiamos el delimitador.
- Control Flow Constructs: Documentación oficial sobre
IF
,CASE
,LOOP
,WHILE
,REPEAT
. - Condition Handling: Todo sobre
DECLARE HANDLER
para el manejo de errores.
- Otros Recursos:
- W3Schools SQL Functions: Un buen punto de partida con ejemplos básicos.
🚀 Tarea Práctica del Módulo 4
Section titled “🚀 Tarea Práctica del Módulo 4”¡Es hora de poner en práctica estas poderosas herramientas!
-
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.
- Obtén el nombre completo de todos los clientes en mayúsculas y la longitud de su nombre (
-
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.
- Crea una consulta que muestre todas las ventas realizadas en el último mes, formateando la
-
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 menos2
ventas. - Identifica el producto más vendido (por cantidad) y el producto que generó más ingresos (por
cantidad * precio_venta
).
- 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
-
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 elTOTAL_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 (¡usaDECLARE HANDLER
aquí!).
- Usa tus nuevas funciones en una consulta que muestre
ID_CLIENTE
,NOMBRE_CLIENTE
,TOTAL_GASTADO
y elTIPO_CLIENTE
para todos los clientes.
- Implementa la función
-
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! 🚀