Saltar a contenido

6 – Triggers

Un trigger (disparador) es un mecanismo automático que se ejecuta como respuesta a un evento sobre una tabla o vista. Puede activarse con operaciones INSERT, UPDATE, DELETE o incluso DDL (como CREATE TABLE o ALTER). En resumen:

Un trigger actúa como un "reaccionador" dentro de la base de datos: “Cuando ocurra X en la tabla Y, ejecuta automáticamente el código Z.”

¿Para qué sirven los triggers?

  • Mantener integridad lógica entre tablas (por ejemplo, borrar líneas de pedido al borrar un pedido).
  • Auditar cambios: registrar en una tabla quién modificó un registro y cuándo.
  • Validar o corregir datos antes de guardarlos (convertir texto, comprobar rangos, etc.).
  • Sincronizar información: actualizar totales o estadísticas al cambiar datos base.

Tipos de triggers en PostgreSQL

Según el momento

Tipo Se ejecuta... Uso típico
BEFORE Antes de la operación Validar, modificar o impedir cambios
AFTER Después de la operación Registrar cambios, propagar datos
INSTEAD OF En lugar de la operación (solo vistas) Personalizar vistas actualizables

Según el alcance

Tipo Actúa sobre... Ejemplo
FOR EACH ROW Cada fila afectada Registrar log por cada registro insertado
FOR EACH STATEMENT Una vez por sentencia Calcular totales tras un UPDATE masivo

Estructura general de un trigger

Un trigger tiene dos componentes:

1- Una función PL/pgSQL que define la lógica.

2- Una declaración CREATE TRIGGER que la asocia a una tabla y evento.

Paso 1. Crear tabla principal

CREATE TABLE empleados (
    id SERIAL PRIMARY KEY,
    nombre TEXT,
    salario NUMERIC(10,2)
);

Paso 2. Crear tabla de auditoría

CREATE TABLE log_empleados (
    id SERIAL PRIMARY KEY,
    empleado_id INT,
    accion TEXT,
    fecha TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    usuario TEXT
);

Paso 3. Crear la función trigger

CREATE OR REPLACE FUNCTION registrar_cambio_empleado()
RETURNS TRIGGER AS $$
BEGIN
    IF TG_OP = 'INSERT' THEN
        INSERT INTO log_empleados (empleado_id, accion, usuario)
        VALUES (NEW.id, 'INSERT', current_user);

    ELSIF TG_OP = 'UPDATE' THEN
        INSERT INTO log_empleados (empleado_id, accion, usuario)
        VALUES (NEW.id, 'UPDATE', current_user);

    ELSIF TG_OP = 'DELETE' THEN
        INSERT INTO log_empleados (empleado_id, accion, usuario)
        VALUES (OLD.id, 'DELETE', current_user);
    END IF;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Elemento Explicación
TG_OP Operación (INSERT, UPDATE, DELETE)
NEW Fila nueva (INSERT/UPDATE)
OLD Fila anterior (UPDATE/DELETE)
current_user Usuario que ejecutó la acción

Paso 4. Asociar el trigger a la tabla

CREATE TRIGGER tr_log_empleados
AFTER INSERT OR UPDATE OR DELETE
ON empleados
FOR EACH ROW
EXECUTE FUNCTION registrar_cambio_empleado();
Elemento Significado
AFTER Se ejecuta después de la acción.
FOR EACH ROW Actúa por cada fila.
EXECUTE FUNCTION Llama a la función PL/pgSQL definida.

Ejemplo de funcionamiento

INSERT INTO empleados (nombre, salario)
VALUES ('Lucía', 2500.00);

PostgreSQL ejecuta automáticamente:

INSERT INTO log_empleados (empleado_id, accion, usuario)
VALUES (1, 'INSERT', 'juanma');

El log se rellena sin intervención manual del programador.

Ejemplo de trigger BEFORE (validación de datos)

CREATE OR REPLACE FUNCTION validar_salario()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.salario < 1000 THEN
        RAISE EXCEPTION 'El salario mínimo debe ser 1000 euros';
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER tr_validar_salario
BEFORE INSERT OR UPDATE ON empleados
FOR EACH ROW
EXECUTE FUNCTION validar_salario();

Si se intenta insertar un salario menor a 1000, la operación se cancela. El trigger BEFORE actúa antes de escribir los datos.

Ejemplo con INSTEAD OF (vistas actualizables)

CREATE VIEW vista_empleados AS
SELECT id, nombre, salario FROM empleados WHERE salario > 2000;
CREATE OR REPLACE FUNCTION insertar_en_vista()
RETURNS TRIGGER AS $$
BEGIN
    INSERT INTO empleados (nombre, salario) VALUES (NEW.nombre, NEW.salario);
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER tr_insertar_vista
INSTEAD OF INSERT ON vista_empleados
FOR EACH ROW
EXECUTE FUNCTION insertar_en_vista();

Permite ejecutar INSERT sobre una vista que, internamente, inserta en la tabla real.

Variables útiles dentro de un trigger

Variable Descripción
TG_OP Tipo de operación (INSERT, UPDATE, DELETE)
TG_TABLE_NAME Nombre de la tabla afectada
TG_WHEN Momento del trigger (BEFORE, AFTER, INSTEAD OF)
NEW Fila nueva (solo INSERT y UPDATE)
OLD Fila antigua (solo UPDATE y DELETE)
TG_ARGV[] Argumentos pasados al trigger

Buenas prácticas con triggers

  • Usar solo cuando sea necesario, ya que pueden complicar la depuración.
  • Documentar claramente cada trigger y su función.
  • Evitar operaciones pesadas dentro del trigger.
  • Usar AFTER para auditorías y BEFORE para validaciones.
  • Probar y depurar con cuidado, usando RAISE NOTICE para depuración.
RAISE NOTICE 'Se ha insertado el empleado %', NEW.nombre;

Cómo ver y eliminar triggers

Listar triggers de una tabla:

\d nombre_tabla
SELECT tgname, tgtype::regtype, tgfoid::regproc
FROM pg_trigger
WHERE tgrelid = 'empleados'::regclass;

Eliminar un trigger:

DROP TRIGGER tr_log_empleados ON empleados;

Eliminar la función asociada:

DROP FUNCTION registrar_cambio_empleado();