CodeGym /Cursos /SQL SELF /Análisis de errores típicos al desarrollar triggers

Análisis de errores típicos al desarrollar triggers

SQL SELF
Nivel 58 , Lección 4
Disponible

Bueno, colegas estudiantes, ya tenéis conocimientos sobre triggers, sus tipos, cómo funcionan e incluso habéis aprendido a crearlos para distintas tareas. Pero, como suele pasar en programación, entender lo que se puede hacer es importante, pero igual de importante es saber lo que NO hay que hacer. Hoy vamos a ver los errores típicos que cometen los desarrolladores al trabajar con triggers, para que podáis evitarlos y ahorraros un par de horas, o incluso días, de debugging.

Recursión de triggers: el trigger se llama a sí mismo

Este es, probablemente, el error más popular entre los que empiezan. Imagina que creas un trigger que actualiza el valor de una columna de la tabla, por ejemplo, last_modified. Pero en cuanto ocurre ese cambio, la propia operación de update vuelve a lanzar el trigger. Esto es un bucle infinito y, como resultado, tu servidor se cae con un error de stack overflow.

Ejemplo:

CREATE OR REPLACE FUNCTION update_last_modified()
RETURNS TRIGGER AS $$
BEGIN
    -- Actualizamos el campo last_modified
    UPDATE my_table
    SET last_modified = NOW()
    WHERE id = NEW.id;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER after_update
AFTER UPDATE ON my_table
FOR EACH ROW
EXECUTE FUNCTION update_last_modified();

¿Qué está mal aquí? La operación UPDATE dentro de la función llama al mismo trigger que la creó. Voilà, tenemos un bucle infinito.

Cómo evitarlo:

Usa la variable OLD y compara los valores antes de hacer cambios:

CREATE OR REPLACE FUNCTION update_last_modified_safe()
RETURNS TRIGGER AS $$
BEGIN
    -- Comprobamos si el valor ha cambiado
    IF NEW.last_modified IS DISTINCT FROM OLD.last_modified THEN
        NEW.last_modified = NOW();
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Asegúrate de no lanzar operaciones innecesarias dentro del trigger.

Mal uso de OLD y NEW

Estas variables son tus colegas cuando trabajas con triggers, pero en manos inexpertas pueden ser una fuente de dolores de cabeza. OLD guarda los datos antes de que se cambie la fila, y NEW los datos que se van a guardar después del cambio.

El error suele pasar por interpretar mal o intentar usar las variables donde no están disponibles. Por ejemplo, si trabajas con un trigger BEFORE INSERT, OLD no estará disponible — la fila se está creando justo ahora.

Ejemplo de error:

-- Esto dará error porque OLD no existe en un insert
CREATE OR REPLACE FUNCTION log_inserts()
RETURNS TRIGGER AS $$
BEGIN
    INSERT INTO audit_log (old_data, new_data)
    VALUES (OLD.my_column, NEW.my_column);
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Cómo evitarlo:

Elige bien dónde usar OLD y NEW:

  • OLD está disponible en operaciones UPDATE y DELETE.
  • NEW está disponible en operaciones INSERT y UPDATE.

Múltiples triggers para una misma operación

En PostgreSQL puedes crear varios triggers para la misma operación y tabla. Parece útil, pero en la práctica puede ser un caos si los triggers empiezan a chocar entre sí o modifican los mismos datos.

Ejemplo:

-- Trigger 1
CREATE OR REPLACE FUNCTION trigger_one()
RETURNS TRIGGER AS $$
BEGIN
    -- Lógica del trigger 1
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Trigger 2
CREATE OR REPLACE FUNCTION trigger_two()
RETURNS TRIGGER AS $$
BEGIN
    -- Lógica del trigger 2
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Crear dos triggers
CREATE TRIGGER trigger_one AFTER INSERT ON my_table EXECUTE FUNCTION trigger_one();
CREATE TRIGGER trigger_two AFTER INSERT ON my_table EXECUTE FUNCTION trigger_two();

Ambos triggers se lanzarán al insertar una fila en la tabla my_table. Si su lógica no está bien sincronizada, puede dar resultados impredecibles.

Cómo evitarlo:

  • Planifica la arquitectura de tus triggers con antelación.
  • Si los triggers tocan la misma lógica, júntalos en un solo trigger.

Problemas de rendimiento

Los triggers añaden cálculos extra a cada operación con la que están relacionados. Si usas triggers en tablas con muchas operaciones o registros, puede bajar mucho el rendimiento.

Ejemplo erróneo:

CREATE OR REPLACE FUNCTION heavy_trigger_function()
RETURNS TRIGGER AS $$
BEGIN
    -- Operación pesada que se ejecuta en cada update de la fila
    PERFORM some_heavy_query();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER performance_killer AFTER UPDATE ON huge_table EXECUTE FUNCTION heavy_trigger_function();

Cómo evitarlo:

  • Minimiza la lógica que ejecutas en el trigger. Si necesitas hacer algo pesado, piensa en moverlo a una tarea en background.
  • Usa restricciones para ejecutar el trigger, añadiendo la condición WHEN:
CREATE TRIGGER optimized_trigger
AFTER UPDATE ON my_table
WHEN (OLD.column_name IS DISTINCT FROM NEW.column_name)
EXECUTE FUNCTION light_function();

Triggers y transacciones

Los triggers se ejecutan dentro de la transacción que inicia tu consulta. Si ocurre un error dentro del trigger, toda la transacción se revierte. Esto puede ser útil en algunos casos, pero puede dar problemas inesperados si no tienes bien pensada la gestión de errores.

Ejemplo de error:

CREATE OR REPLACE FUNCTION error_prone_trigger()
RETURNS TRIGGER AS $$
BEGIN
    -- Error generado
    RAISE EXCEPTION '¡Algo salió mal!';
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Si este trigger se ejecuta, la transacción de tu consulta principal se revierte.

Cómo evitarlo:

Añade manejo de errores en los triggers para minimizar el impacto en la transacción principal:

CREATE OR REPLACE FUNCTION safe_trigger()
RETURNS TRIGGER AS $$
BEGIN
    BEGIN
        -- Código que puede lanzar un error
        INSERT INTO another_table VALUES (NEW.data);
    EXCEPTION
        WHEN OTHERS THEN
            RAISE NOTICE 'Ocurrió un error, pero lo manejamos sin drama.';
    END;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Recomendaciones prácticas

  1. Haz los triggers lo más simples posible. Si te parece que el trigger es demasiado grande o complicado, seguramente deberías dividirlo en funciones separadas o repensar la lógica.

  2. Siempre prueba los triggers con pocos datos primero. Antes de conectar un trigger a una tabla crítica, pruébalo en datos de test.

  3. Documenta los triggers. Dentro de unos meses, tú o tus compis podéis olvidar por qué se creó tal o cual trigger. Una documentación clara te ahorrará dolores de cabeza.

  4. Evita triggers para tareas que puedas resolver en la app. Los triggers van genial para automatizar tareas que necesitan ejecutarse al instante, pero usarlos para lógica de negocio compleja puede traerte problemas en el futuro.

  5. Vigila el rendimiento. Monitoriza siempre el impacto de los triggers en el rendimiento de la base de datos, sobre todo si tus datos o la carga crecen.

Con estos consejos y lo que has aprendido hoy, ya estás listo no solo para crear triggers, sino para escribirlos bien, que funcionen de verdad, sean eficientes y no te den sustos raros.

1
Cuestionario/control
Triggers a nivel de fila y tabla, nivel 58, lección 4
No disponible
Triggers a nivel de fila y tabla
Triggers a nivel de fila y tabla
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION