CREATE TRIGGER în PostgreSQL

Funcții, proceduri și triggere PostgreSQL 8.0+ (EXECUTE FUNCTION: 11+, CREATE OR REPLACE TRIGGER: 14+)

Rulează automat o funcție la INSERT, UPDATE sau DELETE pe un tabel, de exemplu pentru a completa coloana actualizat_la sau pentru jurnal de audit.

Sintaxă SQL
CREATE [OR REPLACE] TRIGGER nume
    {BEFORE | AFTER | INSTEAD OF} {INSERT | UPDATE [OF coloane] | DELETE | TRUNCATE} [OR ...]
    ON tabel
    [FOR EACH {ROW | STATEMENT}]
    [WHEN (condiție)]
    EXECUTE FUNCTION functie_trigger();

Ce face CREATE TRIGGER

Un trigger are două părți: o funcție care returnează tipul special trigger și comanda CREATE TRIGGER, care spune când rulează funcția. În funcție, NEW este rândul nou (la INSERT/UPDATE), OLD rândul vechi (la UPDATE/DELETE), iar TG_OP numele operației.

Un trigger BEFORE ... FOR EACH ROW poate modifica rândul înainte de salvare (returnează NEW modificat) sau poate anula operația pentru acel rând (returnează NULL). Un trigger AFTER vede rândul deja salvat și se folosește pentru efecte secundare: jurnal, actualizarea altor tabele.

WHEN (...) limitează când rulează triggerul, de exemplu doar dacă prețul s-a schimbat: WHEN (OLD.pret IS DISTINCT FROM NEW.pret).

Parametri

Parametru Descriere
BEFORE / AFTER / INSTEAD OF Înainte sau după operație; INSTEAD OF doar pentru view-uri.
FOR EACH ROW / STATEMENT O dată pentru fiecare rând sau o dată pentru toată comanda (implicit STATEMENT).
WHEN (condiție) Rulează funcția doar dacă este adevărată condiția.
EXECUTE FUNCTION Funcția care rulează; înainte de PostgreSQL 11 se scria EXECUTE PROCEDURE.

Ce returnează

Mesajul CREATE TRIGGER.

Exemple

Exemplul 1: Coloana actualizat_la completată automat

CREATE FUNCTION seteaza_actualizat_la() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
    NEW.actualizat_la := now();
    RETURN NEW;
END
$$;

CREATE TRIGGER produse_actualizat_la
    BEFORE UPDATE ON produse
    FOR EACH ROW
    EXECUTE FUNCTION seteaza_actualizat_la();

UPDATE produse SET pret = 6.20 WHERE cod = 'PAINE-01';

Rezultat în psql

CREATE FUNCTION
CREATE TRIGGER
UPDATE 1

Exemplul 2: Jurnal al modificărilor de preț

CREATE TABLE istoric_preturi (
    cod         text,
    operatie    text,
    pret_vechi  numeric(10, 2),
    pret_nou    numeric(10, 2),
    modificat   timestamptz DEFAULT now()
);

CREATE FUNCTION jurnal_pret() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
    INSERT INTO istoric_preturi (cod, operatie, pret_vechi, pret_nou)
    VALUES (NEW.cod, TG_OP, OLD.pret, NEW.pret);
    RETURN NULL;
END
$$;

CREATE TRIGGER produse_jurnal_pret
    AFTER UPDATE OF pret ON produse
    FOR EACH ROW
    WHEN (OLD.pret IS DISTINCT FROM NEW.pret)
    EXECUTE FUNCTION jurnal_pret();

UPDATE produse SET pret = 6.50 WHERE cod = 'PAINE-01';
UPDATE produse SET stoc = 40 WHERE cod = 'PAINE-01';
SELECT cod, operatie, pret_vechi, pret_nou FROM istoric_preturi;

Rezultat în psql

CREATE TABLE
CREATE FUNCTION
CREATE TRIGGER
UPDATE 1
UPDATE 1
   cod    | operatie | pret_vechi | pret_nou 
----------+----------+------------+----------
 PAINE-01 | UPDATE   |       6.20 |     6.50
(1 row)

Exemplul 3: Blocarea unei operații cu o eroare

CREATE FUNCTION interzice_stoc_negativ() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
    IF NEW.stoc < 0 THEN
        RAISE EXCEPTION 'Stocul pentru % nu poate fi negativ', NEW.cod;
    END IF;
    RETURN NEW;
END
$$;

CREATE TRIGGER produse_stoc
    BEFORE INSERT OR UPDATE ON produse
    FOR EACH ROW EXECUTE FUNCTION interzice_stoc_negativ();

UPDATE produse SET stoc = stoc - 100 WHERE cod = 'LAPTE-01';

Rezultat în psql

CREATE FUNCTION
CREATE TRIGGER
ERROR:  Stocul pentru LAPTE-01 nu poate fi negativ
CONTEXT:  PL/pgSQL function interzice_stoc_negativ() line 4 at RAISE

Greșeli frecvente

  • Uiți RETURN NEW într-un trigger BEFORE ... FOR EACH ROW și returnezi NULL: rândul nu mai este salvat, fără nicio eroare.
  • Un trigger AFTER UPDATE modifică același tabel cu UPDATE și declanșează din nou triggerul, la nesfârșit. Folosește BEFORE cu NEW.coloana := ... sau o condiție WHEN.
  • Pui logică de business complicată în triggere: devine greu de urmărit de ce se schimbă datele. Păstrează triggerele scurte și documentate.

Comenzi înrudite