#1 - Programas MySQL
#1 - Programas MySQL
1. Introducción 3
1.1. Tipos de Programas Almacenados 3
1.2. Ventajas de su Uso 3
1.3. Edición de Programas Almacenados 3
1.4. Ejemplos de uso 3
2. Fundamentos de la programación en MySQL 4
2.1. Declaración de Variables 5
2.2. Variables de Usuario 5
2.3. Comentarios en MySQL 5
2.4. Expresiones y Funciones Incorporadas 6
2.5. Operadores en MySQL 6
t
2.6. Bloques de Instrucciones y Control de Flujo 7
yo Instrucción IF:
Instrucción CASE:
2.6 Bucles en MySQL
LOOP + LEAVE:
WHILE:
REPEAT…UNTIL:
2.7. Uso del DELIMITER y estructura general
10
10
11
11
12
12
13
lu
2.8. Ejemplo completo: 13
3. Procedimientos almacenados 14
3.1. Estructura básica de un procedimiento almacenado 14
3.2. Llamada a un procedimiento almacenado 14
3.3. Delimitadores 14
PL
P.Lluyot-25 1
4.3. Ejemplo básico 22
4.4. Parámetros en funciones 23
4.5. Variables locales en funciones 23
4.6. Llamada a funciones 23
4.7. Restricciones y Diferencias Clave con Procedimientos 24
4.8. Ejemplos prácticos adicionales 25
4.9. Modificación y Borrado de Funciones 25
4.10. Aplicaciones comunes 25
Ejercicios Prácticos III 26
Ejercicio propuestos 26
5. Triggers (Disparadores) 26
5.1. ¿Qué es un trigger? 26
5.2. Sintaxis básica 27
5.3. Tipos de triggers 27
5.4. Ejemplo: Auditoría 27
t
5.5. Buenas prácticas 27
6. Tratamiento de errores 27
yo
6.1. Manejo de errores en MySQL
6.2. Sintaxis básica
6.3. Ejemplo típico
6.4. Generación de Errores (SIGNAL)
7. Ejercicios complejos propuestos:
7.1. Gestión de puntos y premios en una tienda de fidelización
27
27
28
29
30
30
lu
7.2. Gestión de Stock Simple 32
PL
P.Lluyot-25 2
1. Introducción
Los programas almacenados en MySQL permiten encapsular lógica de negocio dentro del
servidor de bases de datos. Esto mejora el rendimiento y la seguridad al reducir el tráfico entre
la aplicación y la base de datos.
t
yo
1.1. Tipos de Programas Almacenados
● Procedimientos almacenados: Resuelven problemas al ser llamados, pueden aceptar y
devolver múltiples parámetros.
● Funciones almacenadas: Similares a los procedimientos, pero devuelven un único valor
lu
y pueden usarse dentro de instrucciones SQL.
● Triggers (disparadores): Se activan automáticamente ante eventos en la base de datos.
Imagina una tienda online. Cada vez que un cliente realiza un pedido, es necesario guardar el
pedido, los productos seleccionados, calcular el total y aplicar posibles descuentos. Todo esto
P.Lluyot-25 3
puede hacerse con un procedimiento almacenado que reciba los datos del pedido y realice
todas estas acciones de forma centralizada, segura y automática.
Ventaja: Se encapsula toda la lógica del proceso en un solo lugar, evitando errores
y repitiendo el mismo flujo cada vez que se registra un pedido.
En una base de datos de empleados, es común querer saber la edad actual de una persona a
partir de su fecha de nacimiento. Para eso, se puede usar una función almacenada que reciba la
fecha y devuelva la edad exacta. Esta función se puede reutilizar en informes, filtros,
validaciones, etc.
t
Trigger (disparador): control automático de auditoría
yo
Cuando se modifica información sensible en una tabla (como los datos personales de un
cliente), es fundamental saber quién hizo el cambio y cuándo. Un trigger puede activarse
automáticamente en cada UPDATE y guardar un registro en una tabla de auditoría sin
intervención del programador.
P.Lluyot-25 4
2.1. Declaración de Variables
Sintaxis:
DECLARE nombre_variable1 [,nombre_variable2...] tipo [DEFAULT valor];
Ejemplo:
Tipo de Dato Descripción Ejemplo
t
FLOAT, DOUBLE Números reales aproximados DECLARE promedio FLOAT;
yo
CHAR(N)
VARCHAR(N)
DATE
DATETIME
Cadena de longitud fija
Cadena de longitud
variable
Fecha (YYYY-MM-DD)
Fecha y hora
DECLARE letra CHAR(1) DEFAULT ‘A’;
Las variables de usuario comienzan con @ y tienen alcance a nivel de sesión. No requieren
PL
P.Lluyot-25 5
2.4. Expresiones y Funciones Incorporadas
MySQL dispone de funciones nativas para trabajar con texto, fechas, números y otros tipos de
datos. Estas funciones pueden usarse dentro de procedimientos o consultas para realizar
operaciones comunes.
Los operadores permiten realizar cálculos, comparar valores y construir condiciones lógicas.
Son fundamentales en la construcción de expresiones, condiciones IF, filtros WHERE, y más.
t
Operadores Aritméticos
Sirven para realizar operaciones matemáticas básicas.
*
yo
Operador
Suma
Descripción
Resta
Multiplicación
Ejemplo
SET diferencia = a - b;
Operadores de Comparación
Se utilizan para comparar valores. El resultado siempre es TRUE o FALSE.
P.Lluyot-25 6
Operadores Lógicos
Permiten combinar múltiples condiciones lógicas.
Operadores Especiales
Estos operadores permiten crear condiciones más complejas o específicas.
t
Operador Descripción Ejemplo
yo
BETWEEN
IN(...)
Comprueba si un valor está entre
dos valores
Los bloques BEGIN ... END son esenciales en la programación en MySQL: agrupan un
conjunto de instrucciones que se ejecutan como una unidad. Dentro de un bloque se pueden
P.Lluyot-25 7
declarar variables locales, usar estructuras condicionales (IF, CASE), realizar operaciones y
controlar el flujo con bucles (WHILE, REPEAT, LOOP).
t
BEGIN
Instrucciones
END;
yo
lu
PL
P.Lluyot-25 8
END IF;
-- Mostramos el mensaje
SELECT mensaje AS Resultado;
END //
DELIMITER ;
Los bloques también pueden anidarse, permitiendo estructuras más complejas dentro de
procedimientos.
delimiter %%
drop procedure if exists ejemplo_anidado;
t
create procedure ejemplo_anidado()
yo
-- Comenzamos un bloque exterior
BEGIN
-- Declaramos una variable en el bloque exterior
DECLARE edad INT DEFAULT 20;
-- Mostramos el valor de la variable 'edad' del bloque exterior
SELECT 'Valor de edad dentro del bloque exterior:', edad; -- muestra 20
-- Modificamos la variable 'edad' dentro del bloque exterior
SET edad = 25;
lu
-- Mostramos el valor de la variable 'edad' después de la modificación
en el bloque exterior
SELECT 'Valor de edad después de modificación dentro del bloque
exterior:', edad; -- Muestra 25
-- Comenzamos un bloque interior anidado sin declarar 'edad' en el
interior
PL
BEGIN
-- declare edad int default 1; #####DESCOMENTAR ESTA LÍNEA####
-- leemos en el bloque interior el valor de edad:
SELECT 'Valor de edad dentro del bloque interior:', edad; --
Muestra 25
-- No declaramos 'edad' dentro del bloque interior, por lo que
usamos la del bloque exterior
SET edad = 30; -- Modifica la variable 'edad' del bloque exterior
-- Mostramos el valor de la variable 'edad' dentro del bloque
interior
SELECT 'Valor de edad dentro del bloque interior (modificada):',
edad; -- Muestra 30
END;
-- Después de salir del bloque interior, mostramos el valor de la
variable 'edad' del bloque exterior
SELECT 'Valor de edad después de salir del bloque interior (bloque
exterior):', edad; -- Muestra 30*/
END %%
P.Lluyot-25 9
delimiter ;
call ejemplo_anidado();
En el ejemplo anterior ejecuta el código y verifica como cambia el valor de la variable edad en
función de su modificación en el bloque interior o exterior.
Si descomentamos la línea marcada, comprobar el comportamiento de la variable dentro y
fuera del bloque
(clase 1 - hasta aquí)
Instrucción IF:
El comando IF se utiliza para ejecutar instrucciones condicionalmente. Si la expresión
condicional es verdadera, se ejecutan las instrucciones correspondientes.
IF condición THEN
instrucciones;
[ELSEIF condición2 THEN
t
instrucciones;]
[ELSE
yo
instrucciones;]
END IF;
Por ejemplo:
DECLARE
DECLARE
IF edad
edad INT DEFAULT 25;
mensaje VARCHAR(50);
>= 18 THEN
lu
SET mensaje = 'Mayor de edad';
ELSE
SET mensaje = 'Menor de edad';
END IF;
SELECT mensaje;
PL
Ejercicio propuesto:
Crear un procedimiento, donde definimos una variable llamada nota con un valor comprendido
entre 0 y el 10 (admite dos decimales). Emplear la estructura de IF…ELSEIF…ELSE…ENDIF para
mostrar un mensaje al usuario en función de la nota:
- [9,10] → sobresaliente
- [7,9) -> notable
- [6-7) -> bien
- [5-6) -> suficiente
- [0-5) -> suspenso.
Instrucción CASE:
El comando CASE permite manejar múltiples condiciones y elegir cuál ejecutar. Es más
adecuado cuando tienes varias condiciones a evaluar.
CASE
WHEN condición1 THEN instrucciones;
WHEN condición2 THEN instrucciones;
P.Lluyot-25 10
ELSE instrucciones;
END CASE;
Ejemplo:
DECLARE edad INT DEFAULT 25;
DECLARE mensaje VARCHAR(50);
SET mensaje = CASE
WHEN edad >= 18 THEN 'Mayor de edad'
WHEN edad < 18 THEN 'Menor de edad'
ELSE 'Edad no válida'
t
END;
SELECT mensaje;
yo
Ejemplo2:
DECLARE edad INT DEFAULT 25;
DECLARE mensaje VARCHAR(50);
SELECT mensaje;
PL
Ejercicio propuesto:
Usa la instrucción case para poner en texto castellano el día de la semana. Por ejemplo, el día 1
corresponde al Lunes, etc..
Los bucles permiten repetir instrucciones mientras se cumpla una condición. Son útiles para
iterar cálculos, mostrar listados o recorrer valores.
LOOP + LEAVE:
El comando LOOP permite ejecutar un conjunto de instrucciones repetidamente hasta que se
encuentre una instrucción LEAVE que termine el bucle.
[etiqueta:] LOOP
instrucciones;
END LOOP [etiqueta];
P.Lluyot-25 11
Ejemplo: contar del 1 al 10
LOOP_label: LOOP
SET contador = contador + 1;
IF contador >= 10 THEN
LEAVE LOOP_label;
END IF;
END LOOP;
t
-- Mostrar el número
SELECT CONCAT('Número: ', i) AS mensaje;
-- Salir del bucle al llegar a 5
yo
IF i >= 5 THEN
LEAVE cuenta;
END IF;
END LOOP cuenta;
Ejercicio propuesto:
Mostrar por pantalla los 10 primeros múltiplos de 3 (usando LOOP). Almacena los valores en
lu
una tabla temporal y mostrarlos.
WHILE:
El comando WHILE ejecuta un conjunto de instrucciones mientras la expresión booleana sea
verdadera
PL
Ejemplo:
WHILE contador < 10 DO
SET contador = contador + 1;
END WHILE;
Ejercicio propuesto:
Usar un bucle WHILE para mostrar los cuadrados de los primeros 10 números enteros.
REPEAT…UNTIL:
El comando REPEAT...UNTIL ejecuta un bloque de instrucciones hasta que la expresión
booleana se evalúe como verdadera. A diferencia de WHILE, la condición se evalúa después de
la ejecución, por lo que al menos una iteración siempre se realiza.
P.Lluyot-25 12
[etiqueta:] REPEAT
instrucciones;
UNTIL expresión
END REPEAT [etiqueta];
Ejemplo:
DECLARE i INT DEFAULT 1;
REPEAT
SELECT i;
SET i = i + 1;
UNTIL i > 5
END REPEAT;
Ejercicio propuesto:
Queremos generar números de la secuencia de Fibonacci (donde cada número es la suma de los
dos anteriores: 0, 1, 1, 2, 3, 5, 8...) y detenernos justo después de generar el primer número que
sea mayor que 100. Mostraremos la secuencia del número generado mediante una SELECT
t
(usar la función CONCAT para concatenar la secuencia de números)
yo
2.7. Uso del DELIMITER y estructura general
DELIMITER //
-- Comienza el primer bloque
CREATE PROCEDURE ejemplo_edad()
BEGIN
-- Declaración de variables
DECLARE edad INT DEFAULT 25;
DECLARE mensaje VARCHAR(50);
-- Estructura condicional (IF)
IF edad >= 18 THEN
SET mensaje = 'Mayor de edad';
ELSE
SET mensaje = 'Menor de edad';
END IF;
-- Muestra el valor de la variable 'mensaje'
P.Lluyot-25 13
SELECT mensaje;
END//
DELIMITER ;
DELIMITER $$
CREATE PROCEDURE ejemplo_basico()
BEGIN
t
SELECT 'Esto es un procedimiento almacenado';
END $$
yo
DELIMITER ;
Una vez creado un procedimiento, puede ejecutarse con la instrucción CALL. Esta llamada
ejecuta todas las instrucciones que contiene el procedimiento y, si tiene parámetros, permite
enviar valores de entrada o recibir resultados.
lu
CALL ejemplo_basico();
3.3. Delimitadores
Los delimitadores se utilizan para evitar conflictos con el punto y coma (;) que se usa en SQL.
Para definir un procedimiento almacenado correctamente, se usa DELIMITER:
PL
DELIMITER $$
CREATE PROCEDURE test()
BEGIN
SELECT 'Prueba de delimitador';
END $$
DELIMITER ;
Declaración de Variables
DELIMITER $$
P.Lluyot-25 14
CREATE PROCEDURE ejemplo_variables()
BEGIN
DECLARE mi_variable INT DEFAULT 1;
DECLARE texto VARCHAR(50) DEFAULT 'Ejemplo';
SET mi_variable = mi_variable + 5;
SET texto = CONCAT(texto, ' actualizado');
SELECT mi_variable, texto;
END $$
DELIMITER ;
Variables de Usuario
Las variables de usuario comienzan con @ y tienen un alcance a nivel de sesión. Se pueden usar
dentro o fuera de procedimientos almacenados y no requieren declaración previa. Son útiles
para almacenar valores temporales entre consultas o para probar procedimientos.
t
Ejemplo de uso de una variable de usuario en un procedimiento
yo
DELIMITER $$
CREATE PROCEDURE ejemplo_variable_usuario()
BEGIN
SET @contador = 5;
WHILE @contador > 0 DO
SELECT CONCAT('Cuenta regresiva: ', @contador);
SET @contador = @contador - 1;
END WHILE;
lu
END $$
DELIMITER ;
Aunque las variables de usuario (@variable) pueden utilizarse dentro de un programa SQL, no
son recomendables porque:
Por eso, siempre que sea posible, se recomienda usar variables locales declaradas con
DECLARE, que son seguras, privadas y solo existen dentro del procedimiento donde se definen.
Ejercicios Prácticos I
1. Crea un procedimiento (ejemplo1_sp.sql) que muestre por pantalla el texto "esto es un
ejemplo de procedimiento" (donde la cabecera de la columna sea mensaje).
2. Crea un procedimiento (ejemplo2_sp.sql) donde se declaran tres variables internas:
○ Una de tipo entero con valor de 1.
P.Lluyot-25 15
○ Otra de tipo VARCHAR(10) con valor NULL por defecto.
○ Otra de tipo DECIMAL(4,2).
Dentro del procedimiento, modifica los valores de estas variables, realiza operaciones
matemáticas y muestra los resultados con SELECT.
3. Crea un procedimiento (ejemplo3_sp.sql) donde se declaran dos variables internas:
○ Una de tipo CHAR(1).
○ Otra de tipo ENUM('bajo', 'medio', 'alto').
○ Modifica los valores de las variables y muestra el resultado. Luego, provoca errores
asignando valores no permitidos.
4. Crea un procedimiento (ejemplo4_sp.sql) donde se declaran variables internas de tipo
entero y de tipo fecha, con la fecha actual por defecto.
○ Modifica los valores y muestra los resultados.
○ Provoca errores asignando valores incorrectos a las variables.
5. Asignar cualquier valor a una variable definida por el usuario de distintas formas (mediante
una select y mediante la clausula SET)
Crear un procedimiento (ejemplo5_sp.sql) que modifique dicha variable, muestre el valor
de dicha variable tanto dentro del procedimiento, como fuera de él.
t
3.5. Parámetros en Procedimientos Almacenados
yo
Los procedimientos almacenados en MySQL pueden aceptar parámetros para trabajar con
datos dinámicos. Esto permite reutilizar la lógica con diferentes valores de entrada y obtener
distintos resultados sin tener que modificar el código.
MySQL permite definir tres tipos de parámetros, según cómo se comportan durante la
ejecución:
lu
● IN: Se utiliza para enviar un valor al procedimiento.
● OUT: Se utiliza para devolver un valor desde el procedimiento.
● INOUT: Se utiliza tanto para enviar como para recibir un valor.
DELIMITER //
CREATE PROCEDURE ejemplo_parametro(IN nombre_usuario VARCHAR(50))
BEGIN
SELECT * FROM usuarios WHERE nombre = nombre_usuario;
END //
DELIMITER ;
CALL ejemplo_parametros('Juan');
P.Lluyot-25 16
SET mensaje = CONCAT('Hola, ', nombre, '!');
END $$
DELIMITER ;
Imagina que un procedimiento es como una oficina de mensajería. Según el tipo de parámetro,
el comportamiento del "paquete" (el valor) cambia:
● IN ⇒ : Solo entra a la oficina (se usa pero no se modifica fuera).
t
● OUT ⇐ : Solo sale desde la oficina (se genera dentro).
🔁
● INOUT : Entra, se modifica dentro y sale actualizado.
yo
IN ⇒ : solo entra
DELIMITER //
CREATE PROCEDURE saludar(IN nombre_usuario VARCHAR(50))
BEGIN
SELECT CONCAT('Hola, ', nombre_usuario) AS saludo;
END //
lu
DELIMITER ;
Explicación:
● Se le da un valor ('Pepe') al entrar.
PL
P.Lluyot-25 17
CALL contar_usuarios(@total); -- @total se convierte en NULL al entrar, y luego se
rellena con el valor real.
SELECT @total;
Explicación:
● La oficina no espera nada como entrada.
● Dentro se rellena el paquete (total) con un valor.
● IMPORTANTE: Si usas una variable de usuario como @total, al entrar al procedimiento
se inicializa automáticamente como NULL. No importa qué valor tuviera antes.
● Es como enviar un paquete de salida desde la oficina.
t
END //
DELIMITER ;
yo
Llamada al procedimiento con parámetros:
SET @valor = 10;
CALL duplicar(@valor);
SELECT @valor; -- Muestra 20
Explicación:
● La variable llega con un valor, como un paquete.
lu
● Se modifica dentro (duplicar).
● Sale de nuevo con el nuevo valor.
● Es entrada + salida al mismo tiempo.
Ejercicios Prácticos II
6. Crear una tabla llamada usuarios, que almacene varios usuarios y sus passwords.
Crear un procedimiento (ejemplo6_sp.sql) que reciba dos parámetros de entrada (un
usuario y un password) y que inserte un nuevo usuario con esos datos. Comprobar que si
modificamos una de las variables de entrada dentro del procedimiento, fuera de él la
variable no cambia (los parámetros de entrada son una copia de los parámetros
originales).
Crea la tabla usuario con los campos usuario, password, e inserta algunos registros
CREATE TABLE usuarios (
usuario varchar(15) primary key,
password varchar(15) NOT NULL
);
INSERT INTO usuarios values
( 'pepe', '1234'), ( 'juan', '1234') , ('ana', '1111');
P.Lluyot-25 18
○ Llama al procedimiento inicializando dos variables definidas por el usuario (@)
○ Una vez llamado al procedimiento muestra el valor de las variables definidas por el usuario
para ver si han cambiado
7. Crear un procedimiento (ejemplo7_sp.sql) con un parámetro de de salida (paso de
parámetros por variable o referencia) y comprueba que por defecto su valor es Nulo.
Modifica su valor y muestralo tanto dentro del procedimiento como fuera de él.
○ puedes contar el número de usuarios de la tabla de usuarios creada en el apartado anterior y
almacenarla en la variable de salida
○ Llama al procedimiento con una variable definida por el usuario (@) con un valor asignado y
comprueba que en el procedimiento su valor por defecto es null
8. Crea un procedimiento (ejemplo8_sp.sql) con un parámetro de entrada-salida. El
procedimiento debe cambiar el valor de la variable. Realizar una prueba mostrando el valor
de la variable antes y después de llamar al procedimiento.
○ Inicializa una variable de usuario (@)
○ Llama al procedimiento pasandole dicha variable como parámetro de entrada-salida
○ Muestra su valor dentro del procedimiento,
○ Modifica su valor dentro del procedimiento y muestra el valor por pantalla
○ Muestra el valor una vez finalizada la llamada al procedimiento
t
○ forzar algunos errores con los tipos de variables.
9. Crea un procedimiento (ejemplo9_sp.sql) que compruebe si un usuario existe o no en la
);
usuario VARCHAR(50) NOT NULL UNIQUE PRIMARY KEY,
password VARCHAR(64) NOT NULL -- para SHA2 256
lu
INSERT INTO usuarios (usuario, password)
VALUES ('pepe', SHA2('1234', 256)),
('ana', SHA2('hola123', 256));
○ Si el usuario existe (no es necesario comprobar su password), mostrar un mensaje
afirmandolo, de lo contrario mostrar un mensaje indicando su inexistencia,
○ Realizar el ejercicio empleando un SELECT COUNT
○ Realizar el mismo ejercicio empleando SELECT usuario … y asignarlo a una variable..
PL
P.Lluyot-25 19
);
Inserta algún registro en esta tabla para hacer pruebas usando diferentes fechas de nacimiento.
Crea un procedimiento (ejemplo11_sp.sql) Hacer uso de case...when.. para comprobar si
un usuario pasado por parámetros es mayor de edad o no.
12. Crea un procedimiento (ejemplo12_sp.sql) que borre si existe la tabla artículos. Si no
existe debe crearla e insertar 10 artículos haciendo uso de un bucle WHILE...DO
○ Asegúrate que has insertado 10 artículos y no 9 o 11.
13. Hacer un procedimiento (ejemplo13_sp.sql) de forma que cuando un usuario se
autentifique (comprobar que el usuario existe en la BD), actualice la tabla datos_personales
(aumente el número de acceso y almacene la fecha actual). Si por casualidad no existen
datos en la tabla datos_personales para ese usuario, hay que añadirlos.
Mostrar los datos de la tabla datos personales para ese usuario.
14. Crea un procedimiento (ejemplo14_sp.sql) que pasándole un número muestre una select
para cada número comprendido entre el número y 1. Si el número es 0 o negativo no
mostrará nada. Hacer uso de LOOP para implementar dicho procedimiento.
15. Crear un procedimiento (ejemplo15_sp.sql) que pasándole un número, inserte en la tabla
de artículos el artículo con clave el número p, controlar los errores de clave duplicada y
t
error al insertar null como clave. (CONTROL DE ERRORES→hacer más adelante)
yo
Ejercicio propuesto de repaso
Aplicar sanciones por impago de facturas
(más complejo usando procedimiento y sin emplear control de errores)
Tenemos una tabla de clientes y otra de facturas. Queremos sancionar a los clientes que tienen
varias facturas impagadas.
CREATE TABLE clientes (
lu
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(100),
bloqueado BOOLEAN DEFAULT FALSE,
nivel_riesgo ENUM ('Alto','Medio','Bajo') DEFAULT 'Bajo'
);
PL
P.Lluyot-25 20
(3, 30.00, FALSE, '2025-02-11'),
(4, 55.00, TRUE, '2025-03-01'),
(4, 28.00, FALSE, '2025-03-04'),
(4, 36.00, FALSE, '2025-03-05');
Enunciado:
Crear un procedimiento llamado revisar_clientes_morosidad que:
● Revise cuántas facturas impagadas tiene un cliente dado.
● Si tiene más de 3 impagadas, lo marca como bloqueado y aumenta su nivel de riesgo a
"Alto".
● Si tiene entre 2 y 3 impagadas, se establece su riesgo como "Medio".
● Si tiene 1 o ninguna, su riesgo baja a "Bajo" y se asegura de que no esté bloqueado.
● El procedimiento recibe el ID del cliente y devuelve el número de facturas impagadas
y el nuevo estado de riesgo.
t
ALTER PROCEDURE nombre_procedimiento;
yo
Se debe poseer el privilegio de ALTER ROUTINE para poder modificar
procedimientos y funciones. El uso de las distintas opciones se ha expuesto en el
apartado de creación.
4. Funciones
PL
Una función es un bloque de código SQL que realiza una tarea específica y devuelve un único
valor. A diferencia de los procedimientos, las funciones están diseñadas para ser utilizadas
directamente dentro de expresiones en sentencias SQL (como SELECT, WHERE, SET), lo que
las hace ideales para encapsular lógica reutilizable como cálculos, validaciones o conversiones
de datos.
Además, las funciones no pueden modificar datos, pero sí permiten encapsular lógica
reutilizable, como cálculos, validaciones o conversiones. (No podemos usar sentencias INSERT,
UPDATE, CREATE TABLE, etc…, sólo podemos devolver un valor)
P.Lluyot-25 21
DELIMITER //
CREATE FUNCTION nombre_funcion(
param1 TIPO1,
param2 TIPO2,
...
)
RETURNS tipo_retorno
[CHARACTERISTICS ...] -- Opcional: DETERMINISTIC, NOT DETERMINISTIC,
SQL SECURITY, etc.
BEGIN
DECLARE variable_local TIPO;
-- Lógica de la función (puede incluir IF, CASE, bucles, etc.)
-- Debe haber al menos una sentencia RETURN
RETURN valor_a_devolver;
END //
DELIMITER ;
Características adicionales:
t
● DETERMINISTIC: Indica que la función siempre devuelve el mismo resultado
para los mismos parámetros de entrada. Es importante para la optimización y la
Por ejemplo:
DELIMITER $$
PL
Llamada a la función
SELECT cuadrado(3);
P.Lluyot-25 22
A diferencia de los procedimientos las funciones sólo admiten parámetros de entrada (IN). No
pueden tener parámetros OUT o INOUT. Los parámetros se pasan por valor y se utilizan dentro
de la función para realizar los cálculos o la lógica necesaria.
Ejemplo:
DELIMITER //
DROP FUNCTION IF EXISTS calcular_iva //
CREATE FUNCTION calcular_iva(precio DECIMAL(10,2), tasa_iva DECIMAL(4,2))
RETURNS DECIMAL(10,2)
DETERMINISTIC -- Siempre devuelve lo mismo para los mismos inputs
BEGIN
DECLARE iva_calculado DECIMAL(10,2);
SET iva_calculado = precio * tasa_iva;
RETURN iva_calculado;
END //
DELIMITER ;
t
Llamada a la función
SELECT calcular_iva(100.00, 0.21); -- Devuelve 21.00
yo
4.5. Variables locales en funciones
Al igual que en los procedimientos, se pueden declarar variables locales dentro del bloque
BEGIN ... END de una función utilizando DECLARE. Estas variables tienen alcance limitado a la
función y son útiles para almacenar resultados intermedios.
lu
Ejemplo:
DELIMITER //
CREATE FUNCTION evaluar_nota(nota DECIMAL(4,2))
RETURNS VARCHAR(20)
DETERMINISTIC
BEGIN
PL
Llamada a la función
SELECT evaluar_nota(8.5); -- Devuelve ‘Notable’
P.Lluyot-25 23
● Cláusulas SELECT:
SELECT nombre, calcular_iva(precio, 0.21) AS iva FROM productos;
● Cláusulas WHERE:
SELECT nombre FROM alumnos WHERE evaluar_nota(nota_final) = 'Aprobado';
● Sentencias SET:
SET @edad_calculada = calcular_edad('1990-05-15');
● Valores por defecto en CREATE TABLE o ALTER TABLE (con ciertas limitaciones).
t
● Modificación de Datos:
○ Por defecto y por diseño, las funciones no deben realizar operaciones DML
IMPORTANTE:
P.Lluyot-25 24
DELIMITER //
CREATE FUNCTION calcular_edad(fecha_nacimiento DATE)
RETURNS INT
DETERMINISTIC
BEGIN
RETURN TIMESTAMPDIFF(YEAR, fecha_nacimiento, CURDATE());
END //
DELIMITER ;
Llamada a la función
SELECT nombre, calcular_edad(fecha_nac) FROM empleados;
t
DETERMINISTIC
BEGIN
Llamada a la función
SELECT nombre_completo(nom, ape1, ape2) AS empleado FROM personal;
lu
4.9. Modificación y Borrado de Funciones
Modificación
Al igual que los procedimientos, las funciones pueden modificarse o eliminarse. Se requieren
los mismos privilegios (`ALTER ROUTINE`).
PL
Borrado
DROP FUNCTION [IF EXISTS] nombre_funcion;
ALTER FUNCTION` tiene limitaciones. A menudo es más práctico usar `DROP FUNCTION` y
`CREATE FUNCTION` de nuevo
P.Lluyot-25 25
Ejercicios Prácticos III
16. Crear una función (ejemplo16_fun.sql) de manera que reciba una cantidad de euros y
devuelva el valor expresado en pesetas. Llamar a dicha función en el interior de una select.
17. Crea una función (ejemplo17_fun.sql) de forma que pasándole un usuario nos devuelva su
edad actual (debe comprobar su fecha de nacimiento en el caso de existir y calcular su
edad actual).
Ejercicio propuestos
Validar nombre
Crea una función llamada es_nombre_valido que reciba un usuario (campo usuario). La función
deberá devolver TRUE o FALSE si el nombre cumple las siguientes condiciones:
● Tiene al menos 4 caracteres
● No contiene espacios
● No empieza por número
t
(Usar la tabla usuarios creada anteriormente)
yo
Número de acceso de usuarios a partir de una fecha
Crea una función accesos_desde_fecha(fecha DATE) que devuelva el total de accesos
(num_accesos) de los usuarios cuyo ultimo_login sea posterior a esa fecha.
(Usar la tabla datos_personales creada anteriormente)
lu
Porcentaje de usuarios activos
Crea una función porcentaje_usuarios_activos() que devuelva el porcentaje de usuarios que
han iniciado sesión en los últimos 15 días (ultimo_login) respecto al total de usuarios
existentes.
PL
Ejecuta automáticamente una acción cuando ocurre un evento (INSERT, UPDATE, DELETE).
Los triggers permiten reaccionar automáticamente ante cambios en los datos sin intervención
del usuario o del código externo. Son ideales para:
● Auditar cambios
● Validar condiciones antes de insertar o modificar registros
● Mantener datos sincronizados entre tablas
● Registrar automáticamente fechas de actualización
P.Lluyot-25 26
5.2. Sintaxis básica
DELIMITER //
CREATE TRIGGER nombre_trigger
AFTER INSERT ON tabla
FOR EACH ROW
BEGIN
-- Acción
END //
DELIMITER ;
t
5.4. Ejemplo: Auditoría
CREATE TRIGGER auditoria_insert
END;
yo
AFTER INSERT ON usuarios
FOR EACH ROW
BEGIN
INSERT INTO auditoria(tabla, accion, fecha)
VALUES ('usuarios', 'INSERT', NOW());
Tipos de manejadores:
● CONTINUE: continúa tras el error.
● EXIT: sale del bloque actual.
P.Lluyot-25 27
Condiciones que pueden capturarse:
t
Este procedimiento intenta insertar un alumno, pero si falla (por ejemplo, por clave duplicada),
yo
captura el error y muestra un mensaje personalizado.
IN p_id INT,
IN p_nombre VARCHAR(50)
)
BEGIN
DECLARE mensaje VARCHAR(100);
❌
BEGIN
SET mensaje = ' Error: ID duplicado, no se puede insertar.';
SELECT mensaje AS resultado;
END;
⚠️
BEGIN
SET mensaje = ' Error general al insertar usuario.';
SELECT mensaje AS resultado;
END;
P.Lluyot-25 28
-- Intentamos insertar
INSERT INTO usuarios(id, nombre) VALUES(p_id, p_nombre);
✅
-- Si no hay error, mensaje de éxito
SET mensaje = ' Usuario insertado correctamente.';
SELECT mensaje AS resultado;
END $$
DELIMITER ;
t
SIGNAL interrumpe la ejecución normal del programa almacenado y devuelve información de
error al cliente (o al manejador de errores que lo haya llamado).
yo
El código SQLSTATE '45000' está reservado en MySQL para excepciones definidas por el
usuario. Es la forma estándar de indicar un error que no es un error de base de datos
predefinido, sino un problema lógico de nuestro programa (ej: un parámetro no válido, una
condición de negocio que no se cumple, etc.).
ejemplo:
-- Creamos el procedimiento
CREATE PROCEDURE validar_numero_rango (
IN p_numero INT,
IN p_limite_inferior INT,
IN p_limite_superior INT,
OUT mensaje_validacion VARCHAR(100)
)
BEGIN
-- Validamos si el número está fuera del rango permitido
IF p_numero < p_limite_inferior OR p_numero > p_limite_superior THEN
-- Si está fuera del rango, generamos un error personalizado
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = CONCAT('Error: El número ', p_numero, ' está
fuera del rango permitido [', p_limite_inferior, ', ', p_limite_superior,
'].');
-- NOTA: Cuando SIGNAL se ejecuta, el procedimiento termina
inmediatamente.
P.Lluyot-25 29
-- La línea SET mensaje_validacion de abajo NO se ejecutará en caso
de error.
ELSE
-- Si el número está dentro del rango, asignamos un mensaje de éxito
SET mensaje_validacion = CONCAT('El número ', p_numero, ' es
válido.');
END IF;
END //
DELIMITER ;
t
Una tienda tiene un sistema de fidelización de clientes. Cada vez que un cliente realiza una
yo
compra, se le asignan puntos. Cuando alcanza cierto umbral, puede canjear los puntos por un
premio. Además, si un cliente no realiza compras en más de 6 meses, pierde todos sus puntos.
-- Tabla de clientes
CREATE TABLE clientes (
id_cliente INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(100),
lu
fecha_ultima_compra DATE,
puntos_acumulados INT DEFAULT 0
);
-- Tabla de compras
CREATE TABLE compras (
PL
-- Tabla de premios
CREATE TABLE premios (
id_premio INT AUTO_INCREMENT PRIMARY KEY,
descripcion VARCHAR(100),
puntos_necesarios INT
);
P.Lluyot-25 30
id_cliente INT,
id_premio INT,
fecha_entrega DATE,
FOREIGN KEY (id_cliente) REFERENCES clientes(id_cliente),
FOREIGN KEY (id_premio) REFERENCES premios(id_premio)
);
-- Datos de prueba
INSERT INTO clientes (nombre, fecha_ultima_compra,
puntos_acumulados) VALUES
('Ana García', '2024-10-01', 20),
('Luis Pérez', '2023-06-01', 45),
('Marta López', '2025-03-01', 10);
t
('Mochila', 50);
yo
Enunciado:
Diseña un procedimiento almacenado llamado procesar_compra que gestione el sistema de
fidelización de una tienda. Este procedimiento debe:
Este procedimiento debe utilizar condiciones, control de errores y consultas para extraer y
actualizar la información necesaria en varias tablas relacionadas.
P.Lluyot-25 31
7.2. Gestión de Stock Simple
t
CREATE TABLE productos (
id_producto INT AUTO_INCREMENT PRIMARY KEY,
);
yo
nombre_producto VARCHAR(100) NOT NULL UNIQUE, -- Nombre único
para cada producto
stock INT NOT NULL DEFAULT 0 CHECK (stock >= 0) -- Stock no
puede ser negativo
Función:
Crea una función que devuelva el valor actual del stock de un producto, y en el caso de error
devolver el valor -1.
por ejemplo:
SELECT nombre_producto , obtener_stock(id_producto) from productos;
Procedimiento:
Crear un procedimiento llamado anadir_stock que reciba el identificador del producto, la
cantidad a sumar en stock y devuelva un mensaje con el resultado
P.Lluyot-25 32
● Si las validaciones son correctas actualizar el stock y devolver un mensaje de éxito.
t
yo
lu
PL
P.Lluyot-25 33
t
yo
lu
PL
P.Lluyot-25 34