Triggers en MySQL para vistas materializadas

Resumen

¿Necesitas que una tabla resumen se actualice sola cada vez que cambian los datos de origen? Con triggers en MySQL puedes simular materialized views y mantener resúmenes diarios sincronizados sin intervención manual. Esta guía es para quienes ya manejan SQL básico y quieren automatizar agregaciones.

MySQL no tiene vistas materializadas nativas, pero puedes construirlas combinando una tabla resumen, un INSERT ... ON DUPLICATE KEY UPDATE y un trigger que dispare la actualización tras cada cambio en la tabla fuente.

¿Por qué actualizar manualmente una tabla resumen no escala?

Cuando tienes una tabla como ventas_diarias_M con columnas de fecha, conteo de operaciones y total, cada INSERT en la tabla fuente bill_products rompe la consistencia. Tienes que ir, ejecutar la query y rellenar el renglón a mano.

En la clase se muestra el caso con un insert puntual del 11 de marzo de 2025: producto 10, precio 23.98, fecha 2025-03-11 11:23:43. Tras esa inserción, la tabla resumen sigue mostrando el conteo viejo hasta que tú la actualices [02:00].

¿Qué es una vista materializada? Es una tabla que guarda físicamente el resultado de una consulta agregada y se mantiene actualizada conforme cambian los datos de origen. MySQL no la incluye nativa, así que la simulas con tablas y triggers.

¿Cómo resolver el problema con INSERT ON DUPLICATE KEY UPDATE?

El primer paso intermedio es usar un insert select sobre ventas_diarias_M. Pero como la columna date es primary key y por ende unique, repetir la fecha lanza un error de duplicate key [04:30].

La solución elegante es agregar ON DUPLICATE KEY UPDATE con queries anidados:

sql INSERT INTO ventas_diarias_M (date, count, total) SELECT DATE(date_added), COUNT(bill_product_id), SUM(total) FROM bill_products GROUP BY 1 ON DUPLICATE KEY UPDATE count = (SELECT COUNT(*) FROM bill_products WHERE date_added = date), total = (SELECT SUM(total) FROM bill_products WHERE date_added = date);

Cuando la fecha ya existe, MySQL ejecuta el UPDATE con los subqueries y refresca tanto el conteo como el monto total. El detalle fino: como la columna ya es de tipo DATE, no necesitas envolver con DATE() otra vez, evitas la cacofonía y queda más limpio.

¿Qué es un trigger en MySQL y cómo lo programas?

Un trigger es una función que se ejecuta antes o después de un evento sobre una tabla. Los tres eventos principales son INSERT, UPDATE y DELETE, y puedes engancharlos en BEFORE o AFTER. Para vistas materializadas conviene AFTER, porque ya tienes los cambios aplicados en la tabla fuente [07:50].

¿Para qué sirve FOR EACH ROW dentro del trigger?

Un INSERT puede traer varias tuplas en un solo comando. FOR EACH ROW hace que el trigger se dispare una vez por cada fila insertada, no una sola vez por la sentencia completa. Si insertas cuatro tuplas, el trigger corre cuatro veces y procesa cada una con su propia variable NEW [10:15].

¿Qué hace la variable NEW dentro de un trigger?

NEW representa la fila recién insertada o actualizada. Dentro del cuerpo del trigger usas NEW.date_added, NEW.price, etcétera, para acceder a los valores de esa tupla específica.

¿Qué diferencia hay entre BEFORE y AFTER en un trigger? BEFORE se ejecuta antes de que el cambio se aplique a la tabla, útil para validar o transformar datos. AFTER corre cuando el cambio ya está hecho, ideal para propagar actualizaciones a tablas resumen.

¿Por qué necesitas cambiar el DELIMITER al crear un trigger?

Aquí viene la parte espinosa de la sintaxis. Dentro del trigger usas ; para terminar instrucciones internas, pero MySQL también usa ; para terminar la sentencia CREATE TRIGGER completa. Para evitar el conflicto, le indicas a MySQL que cambie temporalmente el delimitador a |:

sql DELIMITER | CREATE TRIGGER matview_insert AFTER INSERT ON bill_products FOR EACH ROW BEGIN INSERT INTO ventas_diarias_M (date, count, total) VALUES ( DATE(NEW.date_added), (SELECT COUNT(*) FROM bill_products WHERE date_added = DATE(NEW.date_added)), (SELECT SUM(total) FROM bill_products WHERE date_added = NEW.date_added) ) ON DUPLICATE KEY UPDATE count = VALUES(count), total = VALUES(total); END | DELIMITER ;

Después de crear el trigger siempre debes regresar el delimitador a ;. Si te quedas escribiendo con |, el resto de tus queries empieza a fallar [15:40].

¿Qué pasa si el trigger tiene errores de sintaxis o lógica?

MySQL guarda el trigger aunque tenga errores que solo aparecen en ejecución, como un nombre de tabla mal escrito. Te enteras del fallo cuando intentas el INSERT real sobre bill_products y la operación se detiene.

Dato importante: por defecto, si el trigger falla, el INSERT original también falla. En la demostración de clase, un typo en el nombre de la tabla impidió que se insertara la tupla con bill_id 12345, y al consultar bill_products no aparecía ningún registro nuevo [19:20].

La solución es simple pero molesta: DROP TRIGGER, corregir el código y volver a crearlo con la sintaxis del delimiter. Tras la corrección, insertar una nueva fila en bill_products con fecha 2025-03-11 11:11:11 propaga automáticamente el conteo y el total a ventas_diarias_M sin tocarla a mano.

¿Qué eventos puedes encadenar con triggers?

La estructura básica se mantiene en los tres eventos clave que usarás para mantener una vista materializada:

  • AFTER INSERT para sumar nuevas filas al resumen.
  • AFTER UPDATE para recalcular cuando cambian valores existentes.
  • AFTER DELETE para restar filas eliminadas y mantener consistencia.

Cada uno resuelve un problema distinto y tiene matices propios sobre cómo acceder a los datos viejos y nuevos. ¿Has implementado triggers en algún proyecto? Cuéntame en los comentarios qué caso resolviste con ellos.