SIGNAL

SIGNAL lanza un error o advertencia personalizada desde un procedimiento almacenado. Es el equivalente a throw en Java o raise en Python. Permite crear mensajes de error descriptivos y específicos para tu lógica de negocio.

Sintaxis

SIGNAL SQLSTATE 'valor'
SET MESSAGE_TEXT = 'mensaje de error',
    MYSQL_ERRNO = codigo;

SQLSTATE para errores personalizados

El SQLSTATE '45000' está reservado para errores definidos por el usuario:

SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Error personalizado';

Ejemplo básico

DELIMITER //
 
CREATE PROCEDURE verificar_edad(IN edad INT)
BEGIN
    IF edad < 0 THEN
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = 'La edad no puede ser negativa';
    ELSEIF edad > 150 THEN
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = 'La edad no parece válida';
    END IF;
 
    SELECT CONCAT('Edad válida: ', edad) AS resultado;
END //
 
DELIMITER ;
CALL verificar_edad(25);
resultado
Edad válida: 25
-- CALL verificar_edad(-5);
-- Error 1644 (45000): La edad no puede ser negativa

Propiedades de SIGNAL

SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Descripción del error',
    MYSQL_ERRNO = 5001,
    TABLE_NAME = 'productos',
    COLUMN_NAME = 'precio';
PropiedadDescripción
MESSAGE_TEXTMensaje descriptivo del error
MYSQL_ERRNOCódigo de error numérico personalizado
TABLE_NAMENombre de tabla relacionada
COLUMN_NAMENombre de columna relacionada
SCHEMA_NAMENombre del esquema
CURSOR_NAMENombre del cursor

Validación de datos

DELIMITER //
 
CREATE PROCEDURE crear_producto(
    IN p_nombre VARCHAR(100),
    IN p_precio DECIMAL(10,2),
    IN p_stock INT,
    IN p_categoria_id INT
)
BEGIN
    -- Validaciones
    IF p_nombre IS NULL OR TRIM(p_nombre) = '' THEN
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = 'El nombre del producto es obligatorio',
            MYSQL_ERRNO = 5001;
    END IF;
 
    IF p_precio IS NULL OR p_precio <= 0 THEN
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = 'El precio debe ser mayor que cero',
            MYSQL_ERRNO = 5002;
    END IF;
 
    IF p_stock IS NULL OR p_stock < 0 THEN
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = 'El stock no puede ser negativo',
            MYSQL_ERRNO = 5003;
    END IF;
 
    -- Verificar que la categoría existe
    IF NOT EXISTS (SELECT 1 FROM categorias WHERE id = p_categoria_id) THEN
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = 'La categoría especificada no existe',
            MYSQL_ERRNO = 5004;
    END IF;
 
    -- Si todas las validaciones pasan, insertar
    INSERT INTO productos (nombre, precio, stock, categoria_id, activo)
    VALUES (p_nombre, p_precio, p_stock, p_categoria_id, TRUE);
 
    SELECT LAST_INSERT_ID() AS nuevo_id, p_nombre AS producto;
END //
 
DELIMITER ;
-- CALL crear_producto('', 100, 10, 6);
-- Error 1644 (45000): El nombre del producto es obligatorio
 
-- CALL crear_producto('Test', -50, 10, 6);
-- Error 1644 (45000): El precio debe ser mayor que cero

SIGNAL como advertencia

Puedes lanzar advertencias en lugar de errores usando un SQLSTATE que empiece con '01':

DELIMITER //
 
CREATE PROCEDURE verificar_stock_minimo(IN prod_id INT)
BEGIN
    DECLARE v_nombre VARCHAR(100);
    DECLARE v_stock INT;
 
    SELECT nombre, stock INTO v_nombre, v_stock
    FROM productos WHERE id = prod_id;
 
    IF v_stock < 10 THEN
        SIGNAL SQLSTATE '01000'
        SET MESSAGE_TEXT = 'Stock bajo, considerar reposición';
    END IF;
 
    SELECT v_nombre AS producto, v_stock AS stock;
END //
 
DELIMITER ;

Capturar errores SIGNAL

DELIMITER //
 
CREATE PROCEDURE ejemplo_capturar_signal()
BEGIN
    DECLARE v_error VARCHAR(200);
 
    DECLARE CONTINUE HANDLER FOR SQLSTATE '45000'
    BEGIN
        GET DIAGNOSTICS CONDITION 1
            v_error = MESSAGE_TEXT;
    END;
 
    -- Esto lanza un SIGNAL
    CALL verificar_edad(-10);
 
    IF v_error IS NOT NULL THEN
        SELECT CONCAT('Error capturado: ', v_error) AS resultado;
    END IF;
END //
 
DELIMITER ;

Condición nombrada con SIGNAL

DELIMITER //
 
CREATE PROCEDURE ejemplo_condicion_signal()
BEGIN
    DECLARE precio_invalido CONDITION FOR SQLSTATE '45000';
 
    IF TRUE THEN  -- Ejemplo simplificado
        SIGNAL precio_invalido
        SET MESSAGE_TEXT = 'El precio no es válido para esta operación';
    END IF;
END //
 
DELIMITER ;

Limpieza

DROP PROCEDURE IF EXISTS verificar_edad;
DROP PROCEDURE IF EXISTS crear_producto;
DROP PROCEDURE IF EXISTS verificar_stock_minimo;
DROP PROCEDURE IF EXISTS ejemplo_capturar_signal;
DROP PROCEDURE IF EXISTS ejemplo_condicion_signal;

En el siguiente artículo veremos RESIGNAL para relanzar o modificar errores dentro de un handler.

Escrito por Eduardo Lázaro