Salta el contingut
 

Triggers

José Manuel Pérez Barberà

Llicència: CC BY-NC-SA 4.0

(Reconeixement - NoComercial - CompartirIgual) 🅭

Introducció

Un trigger (disparador) és un bloc de codi PL/SQL que s'executa automàticament quan ocorre un esdeveniment en la base de dades. És a dir, no cal cridar-lo manualment: s'activa sol quan passa alguna cosa.

Normalment, l'esdeveniment és una operació INSERT, UPDATE o DELETE sobre una taula.

Triggers, procediments i funcions

Tant els triggers com els procediments i les funcions guarden codi PL/SQL en la base de dades. La diferència és qui decideix quan s'executa:

Trigger Procediment o funció
S'executa automàticament S'executa quan es crida
Està associat a una taula o esdeveniment És independent
L'usuari no el pot executar directament Es pot cridar manualment
Ideal per a validacions automàtiques Ideal per a processos controlats

Per tant, si el codi s'ha d'executar sempre que passe alguna cosa, cal un trigger. En canvi, si es vol decidir quan s'executa, cal un procediment.

Moment d'execució

Un trigger s'executa en un moment concret respecte de l'operació INSERT, UPDATE o DELETE que el dispara:

  • BEFORE: abans de l'operació.
  • AFTER: després de l'operació.
  • INSTEAD OF: en lloc de l'operació.

A més, es poden combinar diverses operacions en un mateix trigger, com BEFORE INSERT OR UPDATE.

També cal indicar quantes vegades s'executa:

  • Per fila (FOR EACH ROW): una vegada per cada fila afectada.
  • Per sentència: una sola vegada per sentència, independentment del nombre de files. És l'opció per defecte si no s'indica FOR EACH ROW.

Estructura bàsica d'un trigger (CREATE TRIGGER)

Un trigger sempre té una estructura fixa; només canvia la lògica interna. Es crea amb l'ordre CREATE TRIGGER:

CREATE OR REPLACE TRIGGER nom_trigger
BEFORE INSERT ON taula
FOR EACH ROW
BEGIN
   -- codi PL/SQL
END;
/

Les parts d'un trigger són:

  • Nom: identifica el trigger en la base de dades.
  • Moment (BEFORE, AFTER o INSTEAD OF): indica quan s'executa.
  • Esdeveniment: indica quina operació activa el trigger.
  • Taula o vista: indica sobre quin objecte actua el trigger.
  • FOR EACH ROW: (opcional) indica que el trigger s'executa per cada fila afectada. Si no s'indica, s'executa una sola vegada per sentència.
  • Bloc PL/SQL: és el codi que s'executarà (BEGIN ... END). Pot contindre qualsevol sentència PL/SQL, com condicionals IF, consultes SELECT INTO, assignacions, validacions o insercions en altres taules.

Així, la definició d'un trigger sempre respon a quatre preguntes: com es diu, quan s'executa, per quina operació s'executa i sobre quin objecte actua.

Documentació oficial: CREATE TRIGGER – Oracle DB

Pseudoregistres :NEW i :OLD

:NEW i :OLD són variables especials que contenen la fila afectada per l'operació. Només es poden utilitzar en triggers de tipus FOR EACH ROW, i permeten accedir als valors de cada columna abans i després del canvi:

  • :OLD: conté els valors anteriors al canvi.
  • :NEW: conté els valors posteriors al canvi.

Per exemple, si un UPDATE canvia un salari de 25000 a 30000, :OLD.salary val 25000 i :NEW.salary val 30000.

Com que no totes les operacions tenen valors anteriors i posteriors, la disponibilitat de cada variable depén de l'operació:

Operació :OLD :NEW
INSERT ❌ No existeix ✅ Sí
UPDATE ✅ Sí ✅ Sí
DELETE ✅ Sí ❌ No existeix
Exercici: Trigger de validació

Crea un trigger que:

  1. Es diga trg_validar_salari.
  2. S'execute abans d'inserir o modificar.
  3. Actue sobre employees.
  4. S'execute per cada fila.
  5. Llance un error si el salari és negatiu.
Solució
CREATE OR REPLACE TRIGGER trg_validar_salari
BEFORE INSERT OR UPDATE ON employees
FOR EACH ROW
BEGIN
IF :NEW.salary < 0 THEN
    RAISE_APPLICATION_ERROR(-20001, 'Salari no vàlid');
END IF;
END;
/
Exercici: :NEW i :OLD

Crea un trigger que:

  1. S'execute AFTER UPDATE sobre employees.
  2. S'execute per cada fila.
  3. Si el salari canvia, mostre per pantalla Salari antic: X - Salari nou: Y.
Solució
CREATE OR REPLACE TRIGGER trg_mostrar_canvi_salari
AFTER UPDATE ON employees
FOR EACH ROW
BEGIN
   IF :NEW.salary != :OLD.salary THEN
      DBMS_OUTPUT.PUT_LINE(
         'Salari antic: ' || :OLD.salary ||
         ' - Salari nou: ' || :NEW.salary
      );
   END IF;
END;
/

Triggers i regles de negoci

Els triggers permeten aplicar regles de negoci directament en la base de dades. No obstant això, no totes les regles són bones candidates.

Regles adequades per a triggers

Com a regla pràctica, si una regla s'ha de complir sempre, independentment de l'aplicació que accedisca a les dades, pot ser una bona candidata a trigger. És el cas de:

  • Validacions simples: comprovar que les dades tenen valors vàlids.
  • Control d'integritat addicional: restriccions que no es poden expressar amb CHECK o claus foranes.
  • Auditoria: registrar els canvis en una taula d'historial.
  • Automatització senzilla: assignar dates, valors per defecte, etc.

Regles poc adequades per a triggers

En canvi, si la regla és complexa o pot evolucionar molt, és millor implementar-la en procediments o en la capa d'aplicació. És el cas de:

  • Processos molt complexos: són difícils de depurar si s'executen de manera oculta.
  • Lògica que afecta moltes taules: multiplica els efectes de cada operació.
  • Processos batch o càlculs pesats: alentirien cada operació sobre la taula.
  • Regles que canvien sovint: és més fàcil mantindre-les en l'aplicació.

Riscos dels triggers

Encara que siguen útils, els triggers tenen uns riscos que cal tindre en compte:

  • Efectes ocults: el codi s'executa automàticament i pot ser difícil saber què està passant.
  • Manteniment difícil: si una taula té molts triggers, entendre'n el comportament es complica.
  • Rendiment: si el trigger fa consultes o operacions costoses, alenteix totes les operacions que el disparen.
Exercici: Regles de negoci

Indica si posaries cadascuna d'aquestes regles en un trigger i justifica-ho:

  1. No permetre salaris negatius.
  2. Calcular les nòmines mensuals de tota l'empresa.
  3. Registrar cada canvi de salari.
Solució
  1. Sí, perquè és una validació simple i obligatòria.
  2. No, perquè és un procés complex i massiu (millor un procediment o un procés batch).
  3. Sí, perquè és una auditoria automàtica.

Bones pràctiques i errors comuns

Bones pràctiques

Per reduir els riscos anteriors, és convenient seguir aquestes pautes:

  • Moderació: no abusar dels triggers.
  • Documentació: explicar què fa cada trigger i per què.
  • Simplicitat: mantindre la lògica senzilla.
  • Noms descriptius: utilitzar el prefix trg_....
  • Rendiment: revisar-ne l'impacte abans de posar-lo en producció.

Errors freqüents

Els errors més habituals en treballar amb triggers són:

  • Taula mutant (mutating table): consultar o modificar la mateixa taula dins d'un trigger FOR EACH ROW (vegeu Error de taula mutant).
  • COMMIT dins d'un trigger: no està permés, ja que trenca la gestió de transaccions.
  • Encadenar triggers sense control: un trigger que provoca una acció que activa un altre trigger pot generar comportaments inesperats.
Exercici: Quin error té aquest trigger?
CREATE OR REPLACE TRIGGER trg_error
AFTER INSERT ON employees
FOR EACH ROW
BEGIN
    COMMIT;
END;
/
Solució

No es pot utilitzar COMMIT dins d'un trigger, ja que la gestió de la transacció correspon a la sentència principal.

Error de taula mutant

Un error habitual en Oracle DB és l'error de taula mutant, que es produeix quan un trigger fila a fila intenta llegir la mateixa taula que està modificant.

Per exemple, es vol crear un trigger que comprove el salari mitjà d'un centre de treball cada vegada que s'actualitza un salari a employees, i que llance un error si supera 5000:

CREATE OR REPLACE TRIGGER trg_check_avg_salary
AFTER UPDATE OF salary ON employees
FOR EACH ROW
DECLARE
    v_avg_salary NUMBER;
BEGIN
    -- Intent de llegir la mateixa taula dins del trigger
    SELECT AVG(salary) INTO v_avg_salary
    FROM employees
    WHERE center_id = :NEW.center_id;

    IF v_avg_salary > 5000 THEN
        RAISE_APPLICATION_ERROR(-20001, 'Salari mitjà del centre massa alt');
    END IF;
END;
/

Si s'actualitza un salari, Oracle genera l'error ORA-04091:

UPDATE employees
SET salary = salary + 100
WHERE employee_id = 101;
ORA-04091: table EMPLOYEES is mutating, trigger/function may not see it

El motiu és que un trigger FOR EACH ROW, tant BEFORE com AFTER, no pot llegir la mateixa taula que està canviant, ja que les dades encara estan a mig modificar. Per això, es diu que la taula és «mutant».

La solució habitual és separar la captura de canvis (per fila) i la validació (per sentència):

  1. Un trigger per fila acumula les files modificades en una taula temporal o en un paquet PL/SQL.
  2. Un trigger per sentència, que s'executa quan la modificació ja ha acabat, fa la validació a partir de les files acumulades.

Resum

Un trigger és una ferramenta potent, però s'ha d'utilitzar amb moderació. És adequat quan una regla senzilla s'ha de complir sempre o quan cal una auditoria o una automatització transparent (vegeu Regles adequades per a triggers). En canvi, quan la lògica és complexa, afecta el rendiment o és millor que l'execució siga visible i explícita, és preferible un procediment.

Si tot és automàtic, després és difícil d'entendre.

📌 Aquest document pot quedar desactualitzat després d’imprimir-lo. Pots consultar la versió més recent a la pàgina web.
🌿 Abans d’imprimir aquest document, considera si és realment necessari. Redueix el consum de paper i ajuda a protegir el nostre entorn.