SQL
Tutorial SQL: Manejo de errores y excepciones
Aprende a capturar y manejar errores y excepciones en SQL con MySQL y PostgreSQL para crear aplicaciones robustas y fiables.
Aprende SQL y certifícateCaptura errores
En SQL, la captura de errores es un aspecto fundamental para desarrollar aplicaciones robustas que puedan manejar situaciones inesperadas sin interrumpir su funcionamiento. Cuando ejecutamos consultas o procedimientos almacenados, diversos problemas pueden surgir: desde violaciones de restricciones hasta errores de sintaxis o problemas de conexión.
Estructura básica de captura de errores
Tanto MySQL como PostgreSQL ofrecen mecanismos para detectar y gestionar errores durante la ejecución de código SQL. La estructura básica sigue un patrón similar al de otros lenguajes de programación, aunque con sintaxis específica para cada sistema de gestión de bases de datos.
En MySQL, la estructura para capturar errores dentro de procedimientos almacenados utiliza el bloque DECLARE ... HANDLER
:
DELIMITER //
CREATE PROCEDURE ejemplo_captura_error()
BEGIN
-- Declaración de variables para manejar errores
DECLARE exit_flag BOOLEAN DEFAULT FALSE;
-- Declarar manejador de errores
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
SET exit_flag = TRUE;
SELECT 'Ha ocurrido un error' AS mensaje;
END;
-- Código que podría generar un error
INSERT INTO clientes (nombre, email) VALUES ('Ana López', 'ana@ejemplo.com');
-- Verificar si ocurrió un error
IF NOT exit_flag THEN
SELECT 'Operación completada con éxito' AS mensaje;
END IF;
END //
DELIMITER ;
En PostgreSQL, la estructura es diferente y utiliza bloques BEGIN ... EXCEPTION
:
CREATE OR REPLACE FUNCTION ejemplo_captura_error()
RETURNS TEXT AS $$
BEGIN
-- Código que podría generar un error
INSERT INTO clientes (nombre, email) VALUES ('Ana López', 'ana@ejemplo.com');
RETURN 'Operación completada con éxito';
EXCEPTION
WHEN OTHERS THEN
RETURN 'Ha ocurrido un error: ' || SQLERRM;
END;
$$ LANGUAGE plpgsql;
Tipos de errores en SQL
Para capturar errores de manera efectiva, es importante entender los diferentes tipos que pueden ocurrir:
- Errores de sintaxis: Ocurren cuando la estructura de la consulta SQL no es correcta.
- Errores de restricción: Suceden al violar reglas como claves únicas o restricciones de integridad referencial.
- Errores de datos: Aparecen al intentar insertar datos incompatibles con el tipo de columna.
- Errores de conexión: Ocurren cuando hay problemas para conectar con la base de datos.
- Errores de tiempo de ejecución: Surgen durante la ejecución de procedimientos o funciones.
Captura de errores específicos
En lugar de capturar todos los errores de manera genérica, es más eficiente manejar tipos específicos de errores con respuestas adaptadas a cada situación.
En MySQL, podemos capturar códigos de error específicos:
DELIMITER //
CREATE PROCEDURE insertar_cliente(IN p_nombre VARCHAR(100), IN p_email VARCHAR(100))
BEGIN
-- Manejador para error de duplicado (código 1062)
DECLARE CONTINUE HANDLER FOR 1062
BEGIN
SELECT 'El cliente ya existe en la base de datos' AS mensaje;
END;
-- Manejador para error de columna no puede ser nula (código 1048)
DECLARE CONTINUE HANDLER FOR 1048
BEGIN
SELECT 'Los datos del cliente no pueden estar vacíos' AS mensaje;
END;
-- Intentar insertar el cliente
INSERT INTO clientes (nombre, email) VALUES (p_nombre, p_email);
-- Si no hubo error, mostrar mensaje de éxito
SELECT 'Cliente registrado correctamente' AS mensaje;
END //
DELIMITER ;
En PostgreSQL, podemos capturar excepciones específicas por su nombre:
CREATE OR REPLACE FUNCTION insertar_cliente(p_nombre VARCHAR, p_email VARCHAR)
RETURNS TEXT AS $$
BEGIN
-- Intentar insertar el cliente
INSERT INTO clientes (nombre, email) VALUES (p_nombre, p_email);
RETURN 'Cliente registrado correctamente';
EXCEPTION
WHEN unique_violation THEN
RETURN 'El cliente ya existe en la base de datos';
WHEN not_null_violation THEN
RETURN 'Los datos del cliente no pueden estar vacíos';
WHEN OTHERS THEN
RETURN 'Error desconocido: ' || SQLERRM;
END;
$$ LANGUAGE plpgsql;
Obtención de información del error
Para un diagnóstico efectivo, es útil obtener información detallada sobre el error que ha ocurrido.
En MySQL, podemos usar variables como @error_code
y @error_message
:
DELIMITER //
CREATE PROCEDURE capturar_info_error()
BEGIN
DECLARE error_code VARCHAR(5);
DECLARE error_message VARCHAR(200);
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1
error_code = MYSQL_ERRNO,
error_message = MESSAGE_TEXT;
SELECT CONCAT('Error ', error_code, ': ', error_message) AS info_error;
END;
-- Código que podría generar un error
DELETE FROM tabla_inexistente WHERE id = 1;
END //
DELIMITER ;
En PostgreSQL, podemos acceder a variables especiales dentro del bloque EXCEPTION:
CREATE OR REPLACE FUNCTION capturar_info_error()
RETURNS TEXT AS $$
BEGIN
-- Código que podría generar un error
DELETE FROM tabla_inexistente WHERE id = 1;
RETURN 'Operación completada con éxito';
EXCEPTION
WHEN OTHERS THEN
RETURN 'Error ' || SQLSTATE || ': ' || SQLERRM;
END;
$$ LANGUAGE plpgsql;
Anidación de bloques de captura
Para manejar errores en diferentes niveles de ejecución, podemos anidar bloques de captura de errores.
En MySQL:
DELIMITER //
CREATE PROCEDURE proceso_complejo()
BEGIN
DECLARE exit_handler_outer BOOLEAN DEFAULT FALSE;
-- Manejador de errores externo
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
SET exit_handler_outer = TRUE;
SELECT 'Error en el proceso principal' AS mensaje;
END;
-- Primer bloque de operaciones
BEGIN
DECLARE exit_handler_inner BOOLEAN DEFAULT FALSE;
-- Manejador de errores interno
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
SET exit_handler_inner = TRUE;
SELECT 'Error en el subproceso' AS mensaje;
END;
-- Operación que podría fallar
UPDATE productos SET stock = stock - 1 WHERE id = 1;
IF exit_handler_inner THEN
SELECT 'Continuando después del error en subproceso' AS estado;
END IF;
END;
-- Segunda operación
IF NOT exit_handler_outer THEN
INSERT INTO registro_operaciones (descripcion) VALUES ('Actualización completada');
END IF;
END //
DELIMITER ;
En PostgreSQL:
CREATE OR REPLACE FUNCTION proceso_complejo()
RETURNS TEXT AS $$
DECLARE
resultado TEXT;
BEGIN
-- Bloque externo
BEGIN
-- Bloque interno
BEGIN
-- Operación que podría fallar
UPDATE productos SET stock = stock - 1 WHERE id = 1;
resultado := 'Actualización de stock completada';
EXCEPTION
WHEN OTHERS THEN
resultado := 'Error en subproceso: ' || SQLERRM;
END;
-- Segunda operación
INSERT INTO registro_operaciones (descripcion) VALUES ('Operación registrada');
resultado := resultado || ' | Registro completado';
RETURN resultado;
EXCEPTION
WHEN OTHERS THEN
RETURN 'Error en proceso principal: ' || SQLERRM;
END;
END;
$$ LANGUAGE plpgsql;
Captura de errores en transacciones
La captura de errores es especialmente importante en transacciones, donde necesitamos asegurar que todas las operaciones se completen correctamente o ninguna se aplique.
En MySQL:
DELIMITER //
CREATE PROCEDURE transferencia_segura(
IN cuenta_origen INT,
IN cuenta_destino INT,
IN monto DECIMAL(10,2)
)
BEGIN
DECLARE exit_flag BOOLEAN DEFAULT FALSE;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
SET exit_flag = TRUE;
ROLLBACK;
SELECT 'Error en la transferencia, operación cancelada' AS resultado;
END;
START TRANSACTION;
-- Retirar de cuenta origen
UPDATE cuentas SET saldo = saldo - monto WHERE id = cuenta_origen;
-- Depositar en cuenta destino
UPDATE cuentas SET saldo = saldo + monto WHERE id = cuenta_destino;
IF NOT exit_flag THEN
COMMIT;
SELECT 'Transferencia completada con éxito' AS resultado;
END IF;
END //
DELIMITER ;
En PostgreSQL:
CREATE OR REPLACE FUNCTION transferencia_segura(
cuenta_origen INT,
cuenta_destino INT,
monto DECIMAL
)
RETURNS TEXT AS $$
BEGIN
-- Iniciar transacción
BEGIN
-- Retirar de cuenta origen
UPDATE cuentas SET saldo = saldo - monto WHERE id = cuenta_origen;
-- Depositar en cuenta destino
UPDATE cuentas SET saldo = saldo + monto WHERE id = cuenta_destino;
RETURN 'Transferencia completada con éxito';
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'Error en la transferencia: %', SQLERRM;
RETURN 'Error en la transferencia, operación cancelada';
END;
END;
$$ LANGUAGE plpgsql;
La captura de errores en SQL es una práctica esencial que permite crear aplicaciones más robustas y confiables, capaces de manejar situaciones inesperadas de manera elegante y sin interrumpir el funcionamiento general del sistema.
Manejo excepciones
El manejo de excepciones en SQL representa un nivel superior de control de errores que permite responder de manera estructurada a situaciones anómalas durante la ejecución de procedimientos almacenados y funciones. A diferencia de la simple captura de errores, el manejo de excepciones proporciona mecanismos más sofisticados para identificar, clasificar y responder a diferentes tipos de problemas.
Excepciones en PostgreSQL
PostgreSQL ofrece un sistema de manejo de excepciones robusto inspirado en lenguajes como Ada y PL/SQL. Este sistema permite definir bloques de código con secciones específicas para el manejo de diferentes tipos de excepciones.
La estructura básica para manejar excepciones en PostgreSQL es:
BEGIN
-- Código principal
EXCEPTION
WHEN condición_excepción1 THEN
-- Acciones para manejar excepción1
WHEN condición_excepción2 THEN
-- Acciones para manejar excepción2
WHEN OTHERS THEN
-- Acciones para cualquier otra excepción
END;
PostgreSQL proporciona un conjunto de excepciones predefinidas que podemos utilizar para manejar situaciones específicas:
CREATE OR REPLACE FUNCTION procesar_pedido(id_producto INT, cantidad INT)
RETURNS TEXT AS $$
BEGIN
-- Verificar stock disponible
UPDATE inventario
SET stock = stock - cantidad
WHERE producto_id = id_producto
AND stock >= cantidad;
IF NOT FOUND THEN
RAISE EXCEPTION 'Stock insuficiente o producto no encontrado';
END IF;
-- Registrar pedido
INSERT INTO pedidos (producto_id, cantidad, fecha)
VALUES (id_producto, cantidad, CURRENT_DATE);
RETURN 'Pedido procesado correctamente';
EXCEPTION
WHEN check_violation THEN
RETURN 'Error: La cantidad solicitada excede los límites permitidos';
WHEN foreign_key_violation THEN
RETURN 'Error: El producto especificado no existe';
WHEN OTHERS THEN
RETURN 'Error inesperado: ' || SQLERRM;
END;
$$ LANGUAGE plpgsql;
Excepciones personalizadas en PostgreSQL
PostgreSQL permite crear excepciones personalizadas utilizando la sentencia RAISE
, lo que facilita el manejo de situaciones específicas de negocio:
CREATE OR REPLACE FUNCTION verificar_edad(fecha_nacimiento DATE)
RETURNS BOOLEAN AS $$
DECLARE
edad INT;
BEGIN
edad := EXTRACT(YEAR FROM AGE(CURRENT_DATE, fecha_nacimiento));
IF edad < 18 THEN
RAISE EXCEPTION 'edad_insuficiente'
USING HINT = 'El usuario debe ser mayor de edad',
DETAIL = 'La edad calculada es ' || edad || ' años';
END IF;
RETURN TRUE;
EXCEPTION
WHEN SQLSTATE '45000' THEN -- Código para excepciones definidas por el usuario
RAISE NOTICE 'Verificación fallida: %', SQLERRM;
RETURN FALSE;
END;
$$ LANGUAGE plpgsql;
Manejo de excepciones en MySQL
MySQL implementa el manejo de excepciones a través de handlers, que son bloques de código que se ejecutan cuando se detecta un error específico. Aunque la sintaxis es diferente a PostgreSQL, el concepto es similar.
La estructura básica en MySQL es:
DELIMITER //
CREATE PROCEDURE ejemplo_manejo_excepciones()
BEGIN
-- Declaración de handlers
DECLARE CONTINUE HANDLER FOR 1062
SELECT 'Error: Entrada duplicada' AS mensaje;
DECLARE EXIT HANDLER FOR 1146
SELECT 'Error: Tabla no existe' AS mensaje;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
SELECT 'Error SQL genérico' AS mensaje;
-- Código principal que podría generar excepciones
INSERT INTO usuarios (username, email) VALUES ('usuario1', 'correo@ejemplo.com');
SELECT * FROM tabla_inexistente;
SELECT 'Esta línea solo se ejecuta si no se activó un EXIT handler' AS estado;
END //
DELIMITER ;
MySQL ofrece tres tipos de handlers para manejar excepciones:
- CONTINUE: Después de ejecutar el handler, la ejecución continúa con la siguiente instrucción.
- EXIT: Después de ejecutar el handler, se termina el bloque actual.
- UNDO: Similar a EXIT pero con rollback (disponible en algunas versiones).
Manejo de múltiples condiciones
En MySQL podemos agrupar múltiples condiciones en un solo handler, lo que resulta útil cuando queremos dar la misma respuesta a diferentes tipos de errores:
DELIMITER //
CREATE PROCEDURE procesar_datos()
BEGIN
-- Handler para errores relacionados con restricciones
DECLARE CONTINUE HANDLER FOR 1062, 1048, 1452
BEGIN
SELECT 'Error de integridad de datos' AS mensaje;
-- Registrar el error en una tabla de log
INSERT INTO error_log (codigo, mensaje, fecha)
VALUES (MYSQL_ERRNO(), CONCAT('Error en procesar_datos: ', MYSQL_ERRNO()), NOW());
END;
-- Código principal
INSERT INTO clientes (id, nombre, email) VALUES (1, 'Juan Pérez', 'juan@ejemplo.com');
END //
DELIMITER ;
Propagación de excepciones
En sistemas complejos, a veces es necesario propagar excepciones a niveles superiores para su manejo centralizado. Tanto PostgreSQL como MySQL permiten esta funcionalidad:
En PostgreSQL:
CREATE OR REPLACE FUNCTION subproceso()
RETURNS VOID AS $$
BEGIN
-- Código que podría generar una excepción
PERFORM 1/0; -- División por cero
EXCEPTION
WHEN division_by_zero THEN
-- Propagar la excepción con información adicional
RAISE EXCEPTION 'Error en subproceso: división por cero detectada'
USING ERRCODE = SQLSTATE;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION proceso_principal()
RETURNS TEXT AS $$
BEGIN
-- Llamar al subproceso
PERFORM subproceso();
RETURN 'Proceso completado';
EXCEPTION
WHEN OTHERS THEN
RETURN 'Error capturado en proceso principal: ' || SQLERRM;
END;
$$ LANGUAGE plpgsql;
En MySQL:
DELIMITER //
CREATE PROCEDURE subproceso()
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
-- Registrar el error
INSERT INTO error_log (mensaje) VALUES ('Error en subproceso');
-- Propagar el error
RESIGNAL;
END;
-- Código que podría generar un error
INSERT INTO tabla_inexistente VALUES (1);
END //
CREATE PROCEDURE proceso_principal()
BEGIN
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
SELECT 'Error capturado en proceso principal' AS mensaje;
END;
CALL subproceso();
SELECT 'Proceso completado' AS estado;
END //
DELIMITER ;
Manejo de excepciones con variables de estado
Una técnica avanzada consiste en utilizar variables de estado para controlar el flujo después de una excepción:
DELIMITER //
CREATE PROCEDURE procesar_pedido(IN id_cliente INT, IN id_producto INT, IN cantidad INT)
BEGIN
DECLARE error_ocurrido BOOLEAN DEFAULT FALSE;
DECLARE stock_actual INT;
-- Handler para cualquier error SQL
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
SET error_ocurrido = TRUE;
-- Registrar el error
INSERT INTO log_errores (codigo, mensaje, fecha)
VALUES (MYSQL_ERRNO(), MYSQL_ERROR(), NOW());
END;
-- Verificar stock
SELECT stock INTO stock_actual FROM inventario WHERE producto_id = id_producto;
IF stock_actual < cantidad THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Stock insuficiente';
END IF;
-- Si no hay error, procesar el pedido
IF NOT error_ocurrido THEN
START TRANSACTION;
-- Actualizar inventario
UPDATE inventario SET stock = stock - cantidad WHERE producto_id = id_producto;
-- Crear pedido
INSERT INTO pedidos (cliente_id, producto_id, cantidad, fecha)
VALUES (id_cliente, id_producto, cantidad, NOW());
IF error_ocurrido THEN
ROLLBACK;
SELECT 'Pedido cancelado debido a errores' AS resultado;
ELSE
COMMIT;
SELECT 'Pedido procesado correctamente' AS resultado;
END IF;
ELSE
SELECT 'No se pudo procesar el pedido' AS resultado;
END IF;
END //
DELIMITER ;
Integración con el sistema de errores del cliente
El manejo de excepciones en SQL debe integrarse adecuadamente con el sistema de errores de la aplicación cliente. Esto implica devolver códigos de error y mensajes que la aplicación pueda interpretar:
CREATE OR REPLACE FUNCTION autenticar_usuario(
p_username VARCHAR,
p_password VARCHAR
)
RETURNS JSON AS $$
DECLARE
usuario_id INT;
resultado JSON;
BEGIN
-- Buscar usuario
SELECT id INTO usuario_id
FROM usuarios
WHERE username = p_username AND password_hash = crypt(p_password, password_hash);
IF FOUND THEN
resultado := json_build_object(
'success', TRUE,
'user_id', usuario_id,
'message', 'Autenticación exitosa'
);
ELSE
resultado := json_build_object(
'success', FALSE,
'error_code', 'AUTH_FAILED',
'message', 'Credenciales inválidas'
);
END IF;
RETURN resultado;
EXCEPTION
WHEN OTHERS THEN
RETURN json_build_object(
'success', FALSE,
'error_code', 'SYSTEM_ERROR',
'message', 'Error interno del sistema',
'details', SQLERRM
);
END;
$$ LANGUAGE plpgsql;
El manejo efectivo de excepciones en SQL es una habilidad fundamental para desarrollar sistemas de bases de datos robustos y confiables. Al implementar estrategias adecuadas de manejo de excepciones, podemos crear aplicaciones que respondan elegantemente a situaciones inesperadas, mejorando la experiencia del usuario y facilitando el mantenimiento del sistema.
Mensajes personalizados
La personalización de mensajes de error en SQL es una técnica avanzada que permite mejorar significativamente la experiencia del usuario y facilitar la depuración de aplicaciones. En lugar de mostrar mensajes genéricos o códigos de error crípticos, podemos crear respuestas claras y específicas que comuniquen exactamente qué salió mal y cómo solucionarlo.
Creación de mensajes personalizados en MySQL
En MySQL, podemos generar mensajes personalizados utilizando la sentencia SIGNAL
, que nos permite definir explícitamente el estado SQL y el texto del mensaje:
DELIMITER //
CREATE PROCEDURE verificar_saldo(IN cuenta_id INT, IN monto DECIMAL(10,2))
BEGIN
DECLARE saldo_actual DECIMAL(10,2);
-- Obtener saldo actual
SELECT saldo INTO saldo_actual
FROM cuentas
WHERE id = cuenta_id;
-- Verificar si la cuenta existe
IF saldo_actual IS NULL THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'La cuenta especificada no existe';
END IF;
-- Verificar si hay saldo suficiente
IF saldo_actual < monto THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = CONCAT(
'Saldo insuficiente. Saldo actual: ',
saldo_actual,
', Monto solicitado: ',
monto
);
END IF;
SELECT 'Fondos disponibles' AS mensaje;
END //
DELIMITER ;
En este ejemplo, utilizamos SIGNAL
para generar mensajes informativos que indican exactamente cuál es el problema: si la cuenta no existe o si el saldo es insuficiente. En el segundo caso, incluso incluimos los valores específicos para facilitar la comprensión del error.
Personalización avanzada con SIGNAL
La sentencia SIGNAL
en MySQL permite incluir información adicional mediante varias cláusulas:
DELIMITER //
CREATE PROCEDURE registrar_usuario(
IN p_username VARCHAR(50),
IN p_email VARCHAR(100),
IN p_edad INT
)
BEGIN
-- Validar edad
IF p_edad < 18 THEN
SIGNAL SQLSTATE '45000'
SET
MESSAGE_TEXT = 'El usuario debe ser mayor de edad',
MYSQL_ERRNO = 30001,
COLUMN_NAME = 'edad';
END IF;
-- Validar formato de email
IF p_email NOT LIKE '%@%.%' THEN
SIGNAL SQLSTATE '45000'
SET
MESSAGE_TEXT = 'Formato de email inválido',
MYSQL_ERRNO = 30002,
COLUMN_NAME = 'email';
END IF;
-- Insertar usuario si todas las validaciones pasan
INSERT INTO usuarios (username, email, edad)
VALUES (p_username, p_email, p_edad);
SELECT CONCAT('Usuario ', p_username, ' registrado correctamente') AS mensaje;
END //
DELIMITER ;
En este procedimiento, utilizamos SIGNAL
con atributos adicionales:
MESSAGE_TEXT
: El mensaje descriptivo del errorMYSQL_ERRNO
: Un código de error personalizadoCOLUMN_NAME
: El nombre de la columna relacionada con el error
Estos atributos adicionales permiten a las aplicaciones cliente procesar el error de manera más efectiva y presentar mensajes más útiles al usuario final.
Mensajes personalizados en PostgreSQL
PostgreSQL ofrece la función RAISE
para generar mensajes personalizados con diferentes niveles de severidad:
CREATE OR REPLACE FUNCTION validar_producto(
p_nombre VARCHAR,
p_precio DECIMAL,
p_stock INT
)
RETURNS VOID AS $$
BEGIN
-- Validar nombre
IF length(p_nombre) < 3 THEN
RAISE EXCEPTION 'El nombre del producto debe tener al menos 3 caracteres'
USING HINT = 'Ingrese un nombre más descriptivo';
END IF;
-- Validar precio
IF p_precio <= 0 THEN
RAISE EXCEPTION 'El precio debe ser mayor que cero'
USING DETAIL = 'Valor ingresado: %', p_precio;
END IF;
-- Validar stock
IF p_stock < 0 THEN
RAISE EXCEPTION 'El stock no puede ser negativo'
USING ERRCODE = 'check_violation';
END IF;
-- Si todo está bien, mostrar mensaje informativo
RAISE NOTICE 'Producto validado correctamente: %', p_nombre;
END;
$$ LANGUAGE plpgsql;
PostgreSQL permite diferentes niveles de severidad para los mensajes:
RAISE DEBUG
: Mensajes de depuración (solo visibles si el nivel de depuración está activado)RAISE LOG
: Mensajes informativos que van al registro del servidorRAISE NOTICE
: Mensajes informativos visibles para el clienteRAISE WARNING
: Advertencias que no detienen la ejecuciónRAISE EXCEPTION
: Errores que interrumpen la ejecución
Personalización con parámetros dinámicos
Una técnica útil es incluir valores dinámicos en los mensajes para proporcionar contexto específico:
CREATE OR REPLACE FUNCTION transferir_fondos(
origen_id INT,
destino_id INT,
monto DECIMAL
)
RETURNS TEXT AS $$
DECLARE
saldo_origen DECIMAL;
nombre_origen VARCHAR;
nombre_destino VARCHAR;
BEGIN
-- Obtener información de las cuentas
SELECT saldo, nombre INTO saldo_origen, nombre_origen
FROM cuentas WHERE id = origen_id;
SELECT nombre INTO nombre_destino
FROM cuentas WHERE id = destino_id;
-- Validar que las cuentas existan
IF nombre_origen IS NULL THEN
RAISE EXCEPTION 'La cuenta de origen (ID: %) no existe', origen_id;
END IF;
IF nombre_destino IS NULL THEN
RAISE EXCEPTION 'La cuenta de destino (ID: %) no existe', destino_id;
END IF;
-- Validar saldo suficiente
IF saldo_origen < monto THEN
RAISE EXCEPTION 'Saldo insuficiente en la cuenta de %: %.2f (requerido: %.2f)',
nombre_origen, saldo_origen, monto;
END IF;
-- Realizar la transferencia
UPDATE cuentas SET saldo = saldo - monto WHERE id = origen_id;
UPDATE cuentas SET saldo = saldo + monto WHERE id = destino_id;
RETURN format('Transferencia exitosa: %.2f de %s a %s',
monto, nombre_origen, nombre_destino);
END;
$$ LANGUAGE plpgsql;
En este ejemplo, los mensajes incluyen información contextual como IDs, nombres y cantidades, lo que hace que sean mucho más útiles para diagnosticar problemas.
Mensajes personalizados con códigos de error específicos
Para sistemas más complejos, es útil definir un catálogo de códigos de error personalizados que permitan a las aplicaciones cliente identificar y manejar situaciones específicas:
DELIMITER //
CREATE PROCEDURE procesar_pedido(
IN cliente_id INT,
IN producto_id INT,
IN cantidad INT
)
BEGIN
DECLARE stock_disponible INT;
DECLARE limite_credito DECIMAL(10,2);
DECLARE total_pedido DECIMAL(10,2);
-- Verificar existencia del cliente
IF NOT EXISTS (SELECT 1 FROM clientes WHERE id = cliente_id) THEN
SIGNAL SQLSTATE '45000'
SET
MESSAGE_TEXT = 'Cliente no encontrado',
MYSQL_ERRNO = 40001;
END IF;
-- Verificar existencia del producto
IF NOT EXISTS (SELECT 1 FROM productos WHERE id = producto_id) THEN
SIGNAL SQLSTATE '45000'
SET
MESSAGE_TEXT = 'Producto no encontrado',
MYSQL_ERRNO = 40002;
END IF;
-- Verificar stock
SELECT stock INTO stock_disponible FROM productos WHERE id = producto_id;
IF stock_disponible < cantidad THEN
SIGNAL SQLSTATE '45000'
SET
MESSAGE_TEXT = CONCAT('Stock insuficiente. Disponible: ', stock_disponible),
MYSQL_ERRNO = 40003;
END IF;
-- Calcular total del pedido
SELECT precio * cantidad INTO total_pedido FROM productos WHERE id = producto_id;
-- Verificar límite de crédito
SELECT credito_disponible INTO limite_credito FROM clientes WHERE id = cliente_id;
IF limite_credito < total_pedido THEN
SIGNAL SQLSTATE '45000'
SET
MESSAGE_TEXT = CONCAT(
'Límite de crédito excedido. Disponible: ',
limite_credito,
', Requerido: ',
total_pedido
),
MYSQL_ERRNO = 40004;
END IF;
-- Procesar pedido
INSERT INTO pedidos (cliente_id, producto_id, cantidad, total)
VALUES (cliente_id, producto_id, cantidad, total_pedido);
-- Actualizar stock
UPDATE productos SET stock = stock - cantidad WHERE id = producto_id;
-- Actualizar crédito
UPDATE clientes SET credito_disponible = credito_disponible - total_pedido
WHERE id = cliente_id;
SELECT 'Pedido procesado correctamente' AS mensaje;
END //
DELIMITER ;
En este procedimiento, cada tipo de error tiene un código específico (40001, 40002, etc.), lo que permite a la aplicación cliente identificar exactamente qué tipo de problema ocurrió y responder adecuadamente.
Mensajes personalizados en diferentes idiomas
Para aplicaciones multilingües, podemos implementar un sistema de mensajes en diferentes idiomas:
CREATE TABLE mensajes_error (
codigo VARCHAR(10),
idioma VARCHAR(5),
mensaje TEXT,
PRIMARY KEY (codigo, idioma)
);
INSERT INTO mensajes_error VALUES
('ERR001', 'es', 'El cliente no existe'),
('ERR001', 'en', 'Customer does not exist'),
('ERR002', 'es', 'Saldo insuficiente'),
('ERR002', 'en', 'Insufficient balance');
DELIMITER //
CREATE PROCEDURE obtener_mensaje(
IN p_codigo VARCHAR(10),
IN p_idioma VARCHAR(5)
)
BEGIN
DECLARE v_mensaje TEXT;
-- Intentar obtener mensaje en el idioma solicitado
SELECT mensaje INTO v_mensaje
FROM mensajes_error
WHERE codigo = p_codigo AND idioma = p_idioma;
-- Si no existe, usar inglés como idioma por defecto
IF v_mensaje IS NULL THEN
SELECT mensaje INTO v_mensaje
FROM mensajes_error
WHERE codigo = p_codigo AND idioma = 'en';
END IF;
-- Si aún no existe, usar un mensaje genérico
IF v_mensaje IS NULL THEN
SET v_mensaje = CONCAT('Error desconocido: ', p_codigo);
END IF;
SELECT v_mensaje AS mensaje;
END //
DELIMITER ;
Esta técnica permite centralizar la gestión de mensajes de error y facilita la internacionalización de la aplicación.
Integración con el sistema de logging
Para facilitar la depuración y auditoría, es recomendable integrar los mensajes personalizados con un sistema de registro:
CREATE TABLE log_errores (
id INT AUTO_INCREMENT PRIMARY KEY,
fecha TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
usuario VARCHAR(50),
codigo_error VARCHAR(10),
mensaje TEXT,
detalles JSON
);
DELIMITER //
CREATE PROCEDURE registrar_error(
IN p_codigo VARCHAR(10),
IN p_mensaje TEXT,
IN p_detalles JSON
)
BEGIN
INSERT INTO log_errores (usuario, codigo_error, mensaje, detalles)
VALUES (CURRENT_USER(), p_codigo, p_mensaje, p_detalles);
END //
CREATE PROCEDURE validar_operacion(
IN operacion_tipo VARCHAR(50),
IN operacion_datos JSON
)
BEGIN
DECLARE v_error_mensaje TEXT;
DECLARE v_error_codigo VARCHAR(10);
-- Validar operación
CASE operacion_tipo
WHEN 'TRANSFERENCIA' THEN
IF JSON_EXTRACT(operacion_datos, '$.monto') <= 0 THEN
SET v_error_codigo = 'ERR101';
SET v_error_mensaje = 'El monto de transferencia debe ser positivo';
-- Registrar error
CALL registrar_error(
v_error_codigo,
v_error_mensaje,
operacion_datos
);
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = v_error_mensaje,
MYSQL_ERRNO = 50000;
END IF;
WHEN 'RETIRO' THEN
-- Otras validaciones...
ELSE
SET v_error_codigo = 'ERR999';
SET v_error_mensaje = CONCAT('Tipo de operación desconocido: ', operacion_tipo);
CALL registrar_error(
v_error_codigo,
v_error_mensaje,
operacion_datos
);
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = v_error_mensaje,
MYSQL_ERRNO = 50999;
END CASE;
-- Si llegamos aquí, la operación es válida
SELECT 'Operación validada correctamente' AS mensaje;
END //
DELIMITER ;
Este enfoque permite mantener un registro detallado de todos los errores que ocurren en el sistema, lo que facilita la identificación de patrones y la resolución de problemas recurrentes.
La implementación de mensajes personalizados en SQL es una práctica que mejora significativamente la calidad del software, facilitando tanto el desarrollo como el mantenimiento de aplicaciones basadas en bases de datos. Al proporcionar información clara y específica sobre los errores, ayudamos a los usuarios y desarrolladores a entender y resolver problemas rápidamente.
Otras lecciones de SQL
Accede a todas las lecciones de SQL y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Sql
Introducción Y Entorno
Introducción A Ddl
Introducción Y Entorno
Instalación De Mysql
Introducción Y Entorno
Instalación De Postgresql
Introducción Y Entorno
Tipos De Datos
Introducción Y Entorno
Bases De Datos Y Tablas
Introducción Y Entorno
Sistemas De Gestión De Bases De Datos
Introducción Y Entorno
Tipos De Bases De Datos
Introducción Y Entorno
Definición De Ddl Y Dml
Introducción Y Entorno
Creación Con Create
Sintaxis Dml Crud
Consultas Básicas De Selección: Select Y Where
Sintaxis Dml Crud
Inserción De Datos: Insert Into
Sintaxis Dml Crud
Actualización De Datos: Update
Sintaxis Dml Crud
Eliminación De Datos: Delete
Sintaxis Dml Crud
Introducción A Dml
Sintaxis Dml Crud
Consultar Datos: Select
Sintaxis Dml Crud
Clasificación De Resultados Con Order By
Filtros Y Clasificación
Filtrado De Valores Únicos Con Distinct
Filtros Y Clasificación
Paginación Con Limit Y Offset
Filtros Y Clasificación
Modificar Con Alter
Sintaxis Ddl
Renombrar Con Rename
Sintaxis Ddl
Borrar Con Drop
Sintaxis Ddl
Uso De Funciones Agregadas: Count, Sum, Avg, Max, Min
Funciones Y Agrupación
Agrupación De Resultados Con Group By
Funciones Y Agrupación
Filtrado De Grupos De Resultados Con Having
Funciones Y Agrupación
Funciones Numéricas Y Matemáticas
Funciones Y Agrupación
Funciones De Fecha Y Hora
Funciones Y Agrupación
Funciones De Texto
Funciones Y Agrupación
Many To One
Asociaciones Entre Tablas
One To Many
Asociaciones Entre Tablas
One To One
Asociaciones Entre Tablas
Many To Many
Asociaciones Entre Tablas
Relaciones Entre Tablas
Joins Y Subqueries
Uso De Inner Join
Joins Y Subqueries
Creación Y Uso De Subqueries
Joins Y Subqueries
Left Join Y Right Join
Joins Y Subqueries
Full Join
Joins Y Subqueries
Cross Join Y Self Join
Joins Y Subqueries
Optimización De Consultas
Sintaxis Avanzada
Uso De Índices Y Particiones
Sintaxis Avanzada
Uso De Vistas
Sintaxis Avanzada
Triggers Y Eventos
Sintaxis Avanzada
Particiones
Sintaxis Avanzada
Restricciones E Integridad
Sintaxis Avanzada
Transacciones
Sintaxis Avanzada
Vistas Materializadas
Sintaxis Avanzada
Rollback
Sintaxis Avanzada
Vistas Con Create View
Sintaxis Avanzada
Principios Acid
Sintaxis Avanzada
Manejo De Errores Y Excepciones
Sintaxis Avanzada
Funciones Ventana
Sintaxis Avanzada
Índices
Sintaxis Avanzada
Expresiones De Tabla Comunes (Cte) Con With
Sintaxis Avanzada
Creación Y Uso De Funciones
Programación En Sql
Creación Y Uso De Procedimientos Almacenados
Programación En Sql
Variables Y Control De Flujo
Programación En Sql
Creación Y Manejo De Usuarios Y Roles
Seguridad Y Administración
Asignación Y Gestión De Permisos
Seguridad Y Administración
Copias De Seguridad Y Restauración De Bases De Datos
Seguridad Y Administración
Ejercicios de programación de SQL
Evalúa tus conocimientos de esta lección Manejo de errores y excepciones con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Tipos de datos
Inserción de datos: INSERT INTO
Filtrado de grupos de resultados con HAVING
Uso de índices y particiones
Renombrar tablas y bases de datos: RENAME
Uso de vistas
Uso de INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN
Agrupación de resultados con GROUP BY
Creación y uso de subqueries
Sentencias INSERT
Copias de seguridad y restauración de bases de datos
Uso de INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN
Instalación de MySQL
Relaciones entre tablas
Eliminación de datos: DELETE
Creación de bases de datos y tablas: CREATE DATABASE, CREATE TABLE
Creación y uso de funciones
Creación de tablas e inserción de datos con SQL
Uso de funciones agregadas: COUNT, SUM, AVG, MAX, MIN
Optimización de consultas
Introducción a SQL
Triggers y eventos
Clasificación de resultados con ORDER BY
Alterar la estructura de tablas existentes: ALTER TABLE
Eliminación de datos: DELETE
Instalación de PostgreSQL
Creación y uso de procedimientos almacenados
Consultas básicas de selección: SELECT y WHERE
Vaciar tablas y bases de datos: DROP
Actualización de datos: UPDATE
Creación y manejo de usuarios y roles
Consultas básicas de selección SELECT y WHERE
Creación de bases de datos y tablas
Bases de datos y tablas
Actualización de datos: UPDATE
Relaciones entre tablas
Filtrado de valores únicos con DISTINCT
Asignación y gestión de permisos
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender la importancia de la captura de errores en SQL para mantener la robustez de las aplicaciones.
- Aprender la sintaxis y estructura para manejar errores y excepciones en MySQL y PostgreSQL.
- Diferenciar entre captura genérica y específica de errores, y cómo implementar ambas.
- Implementar mensajes de error personalizados para mejorar la comunicación con el usuario y facilitar la depuración.
- Integrar el manejo de errores con transacciones y sistemas de logging para asegurar la integridad y trazabilidad.