Curso de MySQL
Triggers en MySQL: acciones automáticas en la base de datos
Por Víctor Peña · Publicado el
Hola, ¿cómo están? Continuando con los temas complementarios del curso de MySQL, hoy veremos los triggers: código que se ejecuta solo, sin que nadie lo llame.
Son útiles y peligrosos a partes iguales, así que vamos con las dos caras.
¡Empecemos!
Qué es un trigger
Un trigger —o disparador— es un bloque de código asociado a una tabla, que MySQL ejecuta automáticamente cuando ocurre un INSERT, UPDATE o DELETE sobre ella.
La diferencia con un procedimiento almacenado es esa: el procedimiento se llama con CALL; el trigger se dispara solo.
Anatomía de un trigger
DELIMITER //
CREATE TRIGGER tr_citas_antes_insertar
BEFORE INSERT ON citas
FOR EACH ROW
BEGIN
IF NEW.costo < 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'El costo no puede ser negativo';
END IF;
END //
DELIMITER ;
Las piezas:
BEFOREoAFTER— antes o después de la operación.INSERT,UPDATEoDELETE— qué lo dispara.ON citas— sobre qué tabla.FOR EACH ROW— se ejecuta por cada fila afectada. Si unUPDATEtoca 500 filas, el trigger corre 500 veces.
NEW y OLD
Dentro del trigger tienes acceso a los valores de la fila:
| Evento | NEW |
OLD |
|---|---|---|
INSERT |
Los valores que entran | No existe |
UPDATE |
Los valores nuevos | Los anteriores |
DELETE |
No existe | Los que se borran |
-- En un UPDATE
IF NEW.costo <> OLD.costo THEN
-- el costo cambió
END IF;
En un trigger BEFORE puedes modificar NEW. En uno AFTER no, porque la operación ya ocurrió.
Caso 1: validar y normalizar datos
DELIMITER //
CREATE TRIGGER tr_pacientes_antes_insertar
BEFORE INSERT ON pacientes
FOR EACH ROW
BEGIN
-- Normalizar: quitar espacios y capitalizar
SET NEW.nombre = TRIM(NEW.nombre);
SET NEW.apellido = TRIM(NEW.apellido);
SET NEW.correo = LOWER(TRIM(NEW.correo));
-- Validar
IF NEW.fecha_nacimiento > CURDATE() THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'La fecha de nacimiento no puede ser futura';
END IF;
END //
DELIMITER ;
SIGNAL SQLSTATE '45000' es la forma de lanzar un error personalizado que aborta la operación. El código 45000 es el genérico para errores definidos por el usuario.
Caso 2: auditoría
Este es el uso donde los triggers son realmente insustituibles: registrar quién cambió qué y cuándo.
CREATE TABLE auditoria_citas (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
cita_id INT UNSIGNED NOT NULL,
campo VARCHAR(50) NOT NULL,
valor_anterior VARCHAR(255),
valor_nuevo VARCHAR(255),
usuario VARCHAR(100),
fecha TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
DELIMITER //
CREATE TRIGGER tr_citas_auditoria
AFTER UPDATE ON citas
FOR EACH ROW
BEGIN
IF NEW.estado <> OLD.estado THEN
INSERT INTO auditoria_citas (cita_id, campo, valor_anterior, valor_nuevo, usuario)
VALUES (NEW.id, 'estado', OLD.estado, NEW.estado, USER());
END IF;
IF NEW.costo <> OLD.costo THEN
INSERT INTO auditoria_citas (cita_id, campo, valor_anterior, valor_nuevo, usuario)
VALUES (NEW.id, 'costo', OLD.costo, NEW.costo, USER());
END IF;
END //
DELIMITER ;
Lo valioso: funciona pase lo que pase. Da igual si el cambio vino de tu aplicación, de un script, de phpMyAdmin o de alguien escribiendo SQL a mano. La aplicación puede olvidarse de registrar; el trigger no.
Por eso en sistemas médicos, contables o legales la auditoría suele implementarse así.
Caso 3: mantener datos calculados
-- Añadimos una columna con el total gastado
ALTER TABLE pacientes ADD COLUMN total_gastado DECIMAL(12,2) DEFAULT 0;
DELIMITER //
CREATE TRIGGER tr_citas_actualizar_total
AFTER UPDATE ON citas
FOR EACH ROW
BEGIN
IF NEW.estado = 'atendida' AND OLD.estado <> 'atendida' THEN
UPDATE pacientes
SET total_gastado = total_gastado + NEW.costo
WHERE id = NEW.paciente_id;
END IF;
END //
DELIMITER ;
Es la desnormalización que mencionamos en la lección de optimización: guardar un total precalculado para no sumarlo en cada consulta.
El trigger se encarga de mantenerlo sincronizado.
Caso 4: borrado lógico en cascada
DELIMITER //
CREATE TRIGGER tr_pacientes_borrado_logico
AFTER UPDATE ON pacientes
FOR EACH ROW
BEGIN
IF NEW.eliminado_en IS NOT NULL AND OLD.eliminado_en IS NULL THEN
UPDATE citas
SET estado = 'cancelada'
WHERE paciente_id = NEW.id AND estado = 'programada';
END IF;
END //
DELIMITER ;
Al marcar un paciente como eliminado, sus citas pendientes se cancelan solas.
Gestionar triggers
-- Listar
SHOW TRIGGERS FROM clinica;
-- Ver la definición
SHOW CREATE TRIGGER tr_citas_auditoria;
-- Eliminar
DROP TRIGGER IF EXISTS tr_citas_auditoria;
Igual que con los procedimientos, para modificar uno hay que borrarlo y recrearlo.
Sobre nombres, una convención útil: tr_tabla_momento_evento, como tr_citas_antes_insertar. Cuando tengas quince triggers, saber qué hace cada uno por el nombre vale mucho.
Por qué hay que usarlos con cuidado
Y aquí viene la parte importante, porque los triggers tienen mala fama y en buena medida se la ganaron.
Son invisibles. Este es el problema de fondo. Alguien ejecuta un UPDATE sencillo y se disparan tres triggers que modifican otras cuatro tablas. Nada en la sentencia lo sugiere. Depurar un sistema así es frustrante: los efectos aparecen sin causa aparente.
Se ejecutan por cada fila. Un UPDATE que afecta a 100.000 filas ejecuta el trigger 100.000 veces. Si dentro hace un INSERT, son 100.000 inserciones extra. Operaciones masivas que deberían tardar segundos tardan horas.
Pueden encadenarse. Un trigger sobre citas actualiza pacientes, que tiene otro trigger que actualiza algo más. Seguir esa cadena es difícil, y un bucle infinito es posible.
No se versionan. Igual que los procedimientos, viven en la base y no en Git.
Complican las pruebas. Insertar un registro de prueba puede tener efectos colaterales que no esperabas.
Cuándo sí y cuándo no
Sí conviene:
- Auditoría. Es el caso donde ninguna alternativa da la misma garantía.
- Validaciones críticas que deben cumplirse pase lo que pase.
- Normalización simple de datos, como pasar correos a minúsculas.
No conviene:
- Lógica de negocio. Va en la aplicación, donde se lee, se prueba y se versiona.
- Operaciones pesadas, como enviar notificaciones o llamar a servicios externos.
- Cálculos complejos encadenados entre tablas.
La regla que aplico: un trigger debe ser corto, obvio y sin efectos colaterales sorprendentes. Si necesitas más de veinte líneas, probablemente eso pertenece a la aplicación.
Documéntalos
Como son invisibles, hay una práctica que ahorra muchos disgustos: mantener un archivo en el repositorio con todos los triggers de la base, aunque estén creados en el servidor.
Así quien llegue nuevo al proyecto puede leer qué pasa por debajo sin tener que descubrirlo depurando.
Errores comunes
- Lógica de negocio metida en triggers.
- Operaciones pesadas dentro de un
FOR EACH ROW. - Triggers encadenados que nadie puede seguir.
- No documentarlos, dejando el sistema lleno de comportamiento oculto.
- Modificar
NEWen un triggerAFTER, que no tiene efecto. - Olvidar que existen al hacer una carga masiva, y esperar un rendimiento que no llega.
Para cerrar
Los triggers son la herramienta correcta para una cosa muy concreta: garantizar que algo ocurra siempre, sin importar quién toque los datos. La auditoría es su caso ideal.
Para todo lo demás, la lógica en la aplicación es más fácil de leer, probar y mantener.
En la siguiente lección veremos las copias de seguridad.
Saludos y éxitos.
Norvic Software
Desarrollamos el software que tu empresa necesita
Somos una fábrica de software en Bolivia. Construimos sistemas a medida y aplicaciones móviles, y llevamos Inteligencia Artificial a las empresas que ya tienen un sistema funcionando.
- Chatbots con Inteligencia Artificial
- Creación de agentes de IA
- Integración de IA en tus sistemas
- Desarrollo de software a medida
- Aplicaciones móviles iOS y Android
- Consultoría y asesoramiento técnico
Cotización sin costo · Respuesta directa por WhatsApp