Un trigger ejecuta SQL de forma automática
Un trigger en SQLite es un bloque de SQL guardado que se dispara cada vez que ocurre un evento concreto sobre una tabla determinada. Lo escribes una sola vez y SQLite se encarga del "cuándo" por ti.
La estructura es así:
Fíjate que en ningún momento escribimos un INSERT explícito sobre price_history. Lo hizo el trigger. A partir de ahora, cualquier cambio de precio quedará registrado igual, venga de la CLI, de un script o de una aplicación.
Anatomía de CREATE TRIGGER en SQLite
Vamos a desmenuzar la sintaxis trozo a trozo:
CREATE TRIGGER trigger_name
{ BEFORE | AFTER | INSTEAD OF } { INSERT | UPDATE [ OF column_list ] | DELETE }
ON table_name
[ FOR EACH ROW ]
[ WHEN condition ]
BEGIN
-- una o más sentencias
END;
- Timing —
BEFOREse ejecuta antes del cambio,AFTERdespués, eINSTEAD OFlo reemplaza (solo en vistas). - Evento — qué operación dispara el trigger.
UPDATE OF col1, col2limita losUPDATEa columnas concretas. - Tabla — la tabla bajo vigilancia.
FOR EACH ROW— SQLite solo admite triggers a nivel de fila, así que es implícito. Puedes escribirlo para mayor claridad; no cambia nada.WHEN— una condición opcional. El cuerpo del trigger solo se ejecuta si se cumple.- Cuerpo — una o más sentencias entre
BEGINyEND. Cada una debe terminar con punto y coma.
Esa es toda la gramática. La mayoría de los triggers reales tienen entre cinco y diez líneas.
OLD y NEW: la fila que está cambiando
Dentro del cuerpo, dos pseudo-filas te permiten acceder a los datos:
NEW— la fila entrante. Disponible en triggers deINSERTyUPDATE.OLD— la fila existente. Disponible en triggers deUPDATEyDELETE.
Un trigger de DELETE solo tiene OLD. Uno de INSERT solo tiene NEW. Uno de UPDATE tiene ambos.
La fila eliminada ya no está en accounts, pero sus datos quedaron registrados en deletions antes de desaparecer.
BEFORE: validar o ajustar la fila
Los triggers BEFORE se ejecutan antes de que el cambio se escriba en disco. Son ideales para lanzar un error o normalizar datos:
El segundo INSERT se cancela antes de escribir cualquier fila. RAISE(ABORT, '...') aborta la sentencia actual y revierte todo lo hecho dentro de ella; si necesitas un control más fino, tienes RAISE(FAIL, ...), RAISE(ROLLBACK, ...) y RAISE(IGNORE).
Para validaciones puras de datos, es mejor usar restricciones CHECK: son declarativas y el optimizador las tiene en cuenta. Deja los triggers BEFORE para cuando la regla tenga que consultar otras tablas o hacer algo que un CHECK no pueda expresar.
WHEN: triggers condicionales en SQLite
La cláusula WHEN filtra qué cambios de fila disparan realmente el cuerpo del trigger. Se evalúa fila por fila, una vez que OLD y NEW ya están vinculados:
El primer pedido no pasa el filtro. Los otros dos sí. Sin la cláusula WHEN, cada INSERT habría acabado escribiendo en big_orders, y tendrías que filtrar al leer en vez de al escribir.
INSTEAD OF: cómo hacer escribible una vista
Las vistas son de solo lectura por defecto. Un trigger INSTEAD OF intercepta una escritura sobre la vista y ejecuta tu SQL en su lugar; lo habitual es traducirla a escrituras sobre la tabla (o tablas) subyacentes:
La aplicación interactúa con la vista como si fuera una tabla normal. El trigger se encarga de separar los datos en first_name y last_name por detrás, sin que tengas que preocuparte.
Listar y eliminar triggers en SQLite
Los triggers se guardan en sqlite_master junto con las tablas y los índices:
DROP TRIGGER IF EXISTS name; es la forma segura. Si eliminas la tabla a la que pertenece un trigger, este se borra automáticamente, así que no hace falta limpiarlo aparte.
Trampas que conviene tener en cuenta
Hay varios detalles con los que la gente tropieza la primera vez:
- Los triggers se disparan por fila, no por sentencia. Un
UPDATEque toca 1.000 filas dispara el trigger 1.000 veces. Si el cuerpo del trigger es costoso, la cuenta se acumula muy rápido. - Los triggers se ejecutan dentro de la transacción que los envuelve. Si la sentencia externa hace rollback, lo que escribió el trigger también se revierte. Normalmente es justo lo que quieres, pero significa que un trigger no sirve como vía de escape para un "registra esto pase lo que pase".
- Los triggers recursivos están desactivados por defecto. Un trigger que modifica la misma tabla no se vuelve a disparar a sí mismo a menos que actives
PRAGMA recursive_triggers = ON;. Déjalo apagado salvo que tengas un motivo concreto. - El código de la aplicación puede saltárselos, pero solo si esquiva la base de datos. Mientras toda escritura pase por SQLite, el trigger se va a ejecutar. Incluso los ORMs que hacen batch con SQL crudo los disparan.
- No repartas la lógica de negocio entre un montón de triggers. Son invisibles desde el punto donde llamas a la consulta: cuando alguien intente depurar "¿de dónde salió esta fila?", le tocará hacer grep sobre
sqlite_master. Úsalos para asuntos transversales (logs de auditoría, columnas derivadas, hacer escribibles las vistas) y deja el resto en el código de la aplicación.
Un ejemplo realista de log de auditoría
Vamos a juntar los patrones vistos para registrar todos los cambios de una tabla posts:
Con un solo trigger mantienes updated_at al día y, de paso, escribes el registro de auditoría en un único sitio. El código de la aplicación que ejecuta el UPDATE no necesita enterarse de ninguna de las dos cosas.
Lo que viene: soporte para JSON
Los triggers se encargan de automatizar lo que ocurre alrededor de los eventos de fila. La siguiente pieza avanzada de SQLite tiene que ver con lo que puedes guardar dentro de una fila: JSON. SQLite trae un conjunto completo de funciones JSON para consultar y modificar datos estructurados sin salir de SQL, y de eso va la próxima página.
Preguntas frecuentes
¿Qué es un trigger en SQLite?
Un trigger es un bloque de SQL que se ejecuta automáticamente cuando ocurre un evento concreto sobre una tabla: un INSERT, un UPDATE o un DELETE. Lo defines una sola vez con CREATE TRIGGER y SQLite se encarga de dispararlo cada vez que sucede ese evento. Es la forma habitual de mantener un historial de auditoría, calcular columnas derivadas o aplicar reglas sin depender de que la aplicación se acuerde de hacerlo.
¿Cuál es la diferencia entre BEFORE, AFTER e INSTEAD OF?
BEFORE se dispara antes de aplicar el cambio en la fila, así que es ideal para validar o ajustar los datos entrantes. AFTER se ejecuta cuando el cambio ya está hecho, lo que va perfecto para registrar logs o sincronizar otras tablas. INSTEAD OF solo funciona sobre vistas y reemplaza por completo la operación que se iba a ejecutar, lo que te permite hacer escribibles las vistas.
¿Cómo accedo a la fila que está cambiando dentro del trigger?
Usa NEW.columna para la fila entrante en INSERT y UPDATE, y OLD.columna para la fila ya existente en UPDATE y DELETE. Los triggers de INSERT solo ven NEW, los de DELETE solo ven OLD, y los de UPDATE ven ambos. Estas referencias siempre apuntan a la fila concreta que se está procesando en ese momento.
¿Cómo listo o elimino triggers en SQLite?
Los triggers viven en sqlite_master. Con SELECT name, tbl_name FROM sqlite_master WHERE type = 'trigger'; los ves todos. Para borrar uno, DROP TRIGGER nombre_trigger;, o bien DROP TRIGGER IF EXISTS nombre_trigger; si no estás seguro de que exista. Ten en cuenta que al eliminar una tabla también se eliminan sus triggers.