Disparadores
Código que el gestor ejecuta solo, sin que nadie lo llame, cuando ocurre un evento sobre una tabla: cuándo salta, con qué granularidad, cómo se escribe, qué datos ve y qué no puede hacer.
Qué es un disparador
Un disparador es un bloque de una o varias sentencias asociado a una tabla que el gestor ejecuta por su cuenta cuando ocurre un evento sobre ella. Se parece a un procedimiento en que contiene código procedural guardado en la base, y se diferencia en lo esencial: un procedimiento se llama y un disparador no. Nadie lo invoca; salta solo.
Esa es toda su gracia y todo su peligro. Como no depende de que la aplicación se acuerde de ejecutarlo, la regla se cumple siempre, venga el cambio de donde venga. Y como no aparece en el código de la aplicación, un efecto inesperado obliga a ir a buscarlo al catálogo del gestor. Los usos típicos son la auditoría de cambios, el mantenimiento de datos derivados y la validación de reglas que un CHECK no alcanza a expresar.
El procedimiento se ejecuta porque alguien lo llama; el disparador se ejecuta porque ha pasado algo. Esa es la frontera entre los dos.
Para el examen
Qué es: código asociado a una tabla que el gestor ejecuta ante un evento
Quién lo llama: nadie: se dispara solo
Usos típicos: auditoría, datos derivados y validaciones que un CHECK no alcanza
Cuándo salta: momento y evento
Al crear un disparador hay que decir dos cosas: en qué momento actúa respecto de la sentencia que lo provoca y qué evento lo provoca. El momento admite tres valores y el evento otros tres, y se combinan libremente.
| Momento | Cuándo se ejecuta | Para qué se usa |
|---|---|---|
| BEFORE | Antes de aplicar la sentencia | Validar o corregir los valores que van a entrar, y rechazar la operación si no procede |
| AFTER | Después de aplicar la sentencia | Auditar lo ocurrido y propagar el cambio a otras tablas, ya con los datos definitivos |
| INSTEAD OF | En lugar de la sentencia, que no llega a ejecutarse | Sustituir la operación por otra. Es lo que permite escribir sobre vistas que no serían actualizables |
Los eventos son los tres que modifican datos: INSERT, UPDATE y DELETE. Un mismo disparador puede atender a más de uno. De la combinación de tres momentos con tres eventos salen las nueve posibilidades que se preguntan en el examen, aunque INSTEAD OF se reserva en la práctica a las vistas.
Conviene fijar bien el INSTEAD OF, porque es el que se falla: no añade nada a la sentencia original, la anula. Lo único que se ejecuta es el cuerpo del disparador. El ejemplo clásico es convertir un borrado en otra cosa, de modo que un DELETE sobre una tabla acabe insertando una fila en una tabla de auditoría y dejando el dato intacto.
Para el examen
BEFORE: validar o corregir antes
AFTER: auditar y propagar después
INSTEAD OF: anula la sentencia y solo ejecuta el cuerpo; típico de vistas
Eventos: INSERT, UPDATE y DELETE
Cuántas veces salta: fila o sentencia
La otra decisión es la granularidad, y decide el número de ejecuciones. Un disparador de fila, declarado con FOR EACH ROW, se ejecuta una vez por cada fila afectada. Uno de sentencia, declarado con FOR EACH STATEMENT, se ejecuta una sola vez por sentencia, sin importar a cuántas filas alcance.
El caso que lo aclara todo: un UPDATE que modifica cuarenta filas ejecuta cuarenta veces el disparador de fila y una sola vez el de sentencia. Y hay un detalle que se pregunta: si la sentencia no llega a afectar a ninguna fila, el de fila no se ejecuta ni una vez, mientras que el de sentencia se ejecuta igualmente, porque la sentencia sí ha ocurrido.
La elección no es de gusto. Para auditar el valor anterior y el nuevo de cada registro hace falta el de fila, porque es el único que ve los datos de la fila concreta. Para llevar un contador de operaciones o registrar que se ha ejecutado un mantenimiento basta el de sentencia, que además es mucho más barato.
FOR EACH ROW se ejecuta tantas veces como filas toque la sentencia; FOR EACH STATEMENT, una sola vez por sentencia, incluso si no toca ninguna fila.
Para el examen
FOR EACH ROW: una ejecución por fila afectada
FOR EACH STATEMENT: una por sentencia, aunque no toque ninguna fila
Escribir un disparador
En la creación se juntan las tres decisiones: el momento y el evento, la tabla a la que se asocia y la granularidad. El cuerpo va entre BEGIN y END, igual que el de un procedimiento.
CREATE TRIGGER auditar_cambio_modalidad
AFTER UPDATE ON alumno
FOR EACH ROW
BEGIN
INSERT INTO alumno_auditoria
(id_alumno, modalidad_antes, modalidad_despues, cambiado_el)
VALUES (OLD.id_alumno, OLD.modalidad, NEW.modalidad, CURRENT_TIMESTAMP);
END;
CREATE TRIGGER contar_purgas
AFTER DELETE ON intento
FOR EACH STATEMENT
BEGIN
INSERT INTO purga_log (ejecutada_el) VALUES (CURRENT_TIMESTAMP);
END;Dentro del cuerpo de un disparador de fila el gestor pone a disposición los valores de la fila afectada: OLD guarda cómo estaba antes y NEW cómo queda después. Con los dos delante se puede registrar exactamente qué cambió, que es lo que hace el primer ejemplo. El segundo no necesita ninguno de los dos, porque solo anota que hubo una purga.
Para el examen
Qué reúne CREATE TRIGGER: momento, evento, tabla y granularidad
Cuerpo: entre BEGIN y END
OLD y NEW: el antes y el después, en los disparadores de fila
Las pseudotablas OLD y NEW
Los valores anterior y posterior de la fila se ofrecen a través de unas estructuras que no existen en el esquema y que solo son visibles dentro del disparador. Cada fabricante las nombra a su manera, y esa correspondencia es material de examen.
| Momento del dato | Oracle y PostgreSQL | SQL Server |
|---|---|---|
| Valor anterior al cambio | OLD | DELETED |
| Valor posterior al cambio | NEW | INSERTED |
No siempre están disponibles las dos, y el criterio es simple: existe la que tiene sentido. En un INSERT no hay valor anterior, así que solo hay NEW. En un DELETE no hay valor posterior, así que solo hay OLD. En un UPDATE existen las dos, porque hay un antes y un después. Intentar leer la que no corresponde es un error frecuente.
Hay además una asimetría entre momentos que conviene conocer: en un disparador BEFORE se pueden modificar los valores de NEW y con ello alterar lo que finalmente se graba, mientras que en un AFTER el cambio ya está hecho y NEW es solo de lectura.
Para el examen
Nombres en SQL Server: DELETED e INSERTED
En un INSERT: solo existe NEW
En un DELETE: solo existe OLD
Dónde se puede modificar NEW: solo en los BEFORE
Lo que un disparador no puede hacer
- No acepta parámetros. No hay quien se los pase, porque no lo llama nadie: toda la información que recibe le llega por las pseudotablas y por el contexto de la sentencia.
- No controla la transacción. No puede ejecutar START TRANSACTION, COMMIT ni ROLLBACK.
- No devuelve resultados a quien lanzó la sentencia: comunica lo que tenga que comunicar escribiendo en tablas o provocando un error que aborte la operación.
La razón de la segunda limitación explica las otras dos: el disparador se ejecuta dentro de la transacción de la sentencia que lo ha provocado, no en una propia. Si pudiera confirmar o deshacer por su cuenta, rompería la atomicidad de esa transacción, que es justo lo que la transacción existe para garantizar. Todo lo que hace el disparador se confirma o se deshace junto con la sentencia que lo disparó.
Esa regla tiene una excepción conocida: las transacciones autónomas de Oracle, que permiten que un bloque se confirme aparte del que lo llamó. Se usan precisamente para dejar constancia en una auditoría aunque la operación principal termine deshaciéndose, y son la respuesta a por qué a veces sobrevive el registro de un cambio que nunca llegó a aplicarse.
Queda un riesgo de diseño que no es una prohibición pero se pregunta igual: un disparador que escribe en una tabla que a su vez tiene disparadores desencadena una cascada. El gestor limita la profundidad del anidamiento, y una recursión mal cerrada acaba en error de ejecución.
Para el examen
Lo que no acepta: parámetros
Lo que no devuelve: resultados
Lo que no puede ejecutar: COMMIT ni ROLLBACK: corre dentro de la transacción que lo dispara
Excepción: las transacciones autónomas de Oracle