DECLARE HANDLER

DECLARE HANDLER define qué debe hacer MySQL cuando ocurre una condición específica durante la ejecución de un procedimiento almacenado. Es el mecanismo principal de manejo de errores, similar al try-catch de otros lenguajes.

Sintaxis

DECLARE tipo_handler HANDLER
FOR condicion [, condicion ...]
sentencia;

Tipos de handler

TipoComportamiento
CONTINUEEjecuta la sentencia del handler y continúa la ejecución
EXITEjecuta la sentencia del handler y sale del bloque BEGIN...END actual

Condiciones que se pueden manejar

CondiciónDescripción
SQLSTATE 'valor'Un código SQLSTATE específico
mysql_error_codeUn código de error numérico de MySQL
SQLWARNINGCualquier SQLSTATE que empiece con '01'
NOT FOUNDSQLSTATE '02000', usado con cursores
SQLEXCEPTIONCualquier SQLSTATE que no empiece con '00', '01' o '02'
nombre_condicionUna condición definida con DECLARE CONDITION

CONTINUE HANDLER

DELIMITER //
 
CREATE PROCEDURE insertar_con_handler()
BEGIN
    DECLARE v_error BOOLEAN DEFAULT FALSE;
 
    DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
        SET v_error = TRUE;
 
    -- Intentar insertar un duplicado (si hay UNIQUE en email)
    INSERT INTO clientes (nombre, apellidos, email, ciudad, telefono)
    VALUES ('Test', 'Handler', 'maria.garcia@email.com', 'Madrid', '600000000');
 
    IF v_error THEN
        SELECT 'Error: no se pudo insertar el registro' AS mensaje;
    ELSE
        SELECT 'Registro insertado correctamente' AS mensaje;
    END IF;
END //
 
DELIMITER ;
CALL insertar_con_handler();
mensaje
Error: no se pudo insertar el registro

La ejecución continúa después del error porque usamos CONTINUE HANDLER.

EXIT HANDLER

DELIMITER //
 
CREATE PROCEDURE exit_handler_ejemplo()
BEGIN
    DECLARE v_resultado VARCHAR(100) DEFAULT 'No ejecutado';
 
    BEGIN
        DECLARE EXIT HANDLER FOR SQLEXCEPTION
        BEGIN
            SET v_resultado = 'Error capturado, saliendo del bloque';
        END;
 
        -- Esto genera un error (tabla inexistente)
        INSERT INTO tabla_inexistente VALUES (1);
 
        -- Esta línea NO se ejecuta con EXIT HANDLER
        SET v_resultado = 'Insert exitoso';
    END;
 
    SELECT v_resultado AS resultado;
END //
 
DELIMITER ;
CALL exit_handler_ejemplo();
resultado
Error capturado, saliendo del bloque

Handler para errores específicos

DELIMITER //
 
CREATE PROCEDURE manejar_duplicado(IN p_email VARCHAR(100))
BEGIN
    DECLARE v_mensaje VARCHAR(200);
 
    -- Error 1062: Duplicate entry
    DECLARE CONTINUE HANDLER FOR 1062
        SET v_mensaje = CONCAT('El email ', p_email, ' ya existe');
 
    INSERT INTO clientes (nombre, apellidos, email, ciudad, telefono)
    VALUES ('Nuevo', 'Cliente', p_email, 'Madrid', '600000000');
 
    IF v_mensaje IS NULL THEN
        SET v_mensaje = 'Cliente insertado correctamente';
    END IF;
 
    SELECT v_mensaje AS resultado;
END //
 
DELIMITER ;
CALL manejar_duplicado('maria.garcia@email.com');
resultado
El email maria.garcia@email.com ya existe

Handler con SQLSTATE

DELIMITER //
 
CREATE PROCEDURE handler_sqlstate()
BEGIN
    DECLARE v_msg VARCHAR(200);
 
    -- SQLSTATE 23000: Integrity constraint violation
    DECLARE CONTINUE HANDLER FOR SQLSTATE '23000'
        SET v_msg = 'Violación de restricción de integridad';
 
    -- Intentar insertar con FK inválida
    INSERT INTO pedidos (cliente_id, fecha, total, estado)
    VALUES (9999, CURDATE(), 100.00, 'pendiente');
 
    IF v_msg IS NOT NULL THEN
        SELECT v_msg AS error;
    END IF;
END //
 
DELIMITER ;

Handler con bloque BEGIN...END

El handler puede ejecutar múltiples sentencias:

DELIMITER //
 
CREATE PROCEDURE handler_complejo()
BEGIN
    DECLARE v_error_code INT;
    DECLARE v_error_msg VARCHAR(200);
    DECLARE v_tiene_error BOOLEAN DEFAULT FALSE;
 
    DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
    BEGIN
        GET DIAGNOSTICS CONDITION 1
            v_error_code = MYSQL_ERRNO,
            v_error_msg = MESSAGE_TEXT;
        SET v_tiene_error = TRUE;
    END;
 
    -- Operación que podría fallar
    INSERT INTO tabla_inexistente VALUES (1);
 
    IF v_tiene_error THEN
        SELECT v_error_code AS codigo, v_error_msg AS mensaje;
    END IF;
END //
 
DELIMITER ;
CALL handler_complejo();
codigomensaje
1146Table 'tienda_mysql.tabla_inexistente' doesn't exist

Múltiples handlers

DELIMITER //
 
CREATE PROCEDURE multiples_handlers()
BEGIN
    DECLARE v_msg VARCHAR(200) DEFAULT 'OK';
 
    DECLARE CONTINUE HANDLER FOR 1062
        SET v_msg = 'Registro duplicado';
 
    DECLARE CONTINUE HANDLER FOR 1452
        SET v_msg = 'Clave foránea no válida';
 
    DECLARE CONTINUE HANDLER FOR SQLWARNING
        SET v_msg = 'Advertencia detectada';
 
    -- Lógica del procedimiento...
    SELECT v_msg AS estado;
END //
 
DELIMITER ;

Prioridad de handlers

Cuando ocurre un error, MySQL busca el handler más específico:

  1. Código de error MySQL específico
  2. SQLSTATE específico
  3. SQLEXCEPTION, SQLWARNING o NOT FOUND

Limpieza

DROP PROCEDURE IF EXISTS insertar_con_handler;
DROP PROCEDURE IF EXISTS exit_handler_ejemplo;
DROP PROCEDURE IF EXISTS manejar_duplicado;
DROP PROCEDURE IF EXISTS handler_sqlstate;
DROP PROCEDURE IF EXISTS handler_complejo;
DROP PROCEDURE IF EXISTS multiples_handlers;

En el siguiente artículo veremos DECLARE CONDITION para dar nombres legibles a las condiciones de error.

Escrito por Eduardo Lázaro