I trigger in PostgreSQL si dividono in tre categorie principali:
BEFORE— vengono eseguiti prima dell’operazione principale (tipo prima diINSERT,UPDATEoDELETE). Puoi usarli per bloccare l’operazione o modificare i dati prima che vengano salvati.AFTER— vengono eseguiti dopo che l’operazione principale è finita. Questo tipo si usa spesso per logging, creazione di record collegati o per fare cose che dipendono dal successo dell’operazione.INSTEAD OF— vengono eseguiti al posto dell’operazione vera e propria. Si usano solo sulle view. Per esempio, se qualcuno prova a inserire dati in una view, puoi gestire tutto con un triggerINSTEAD OF.
Trigger BEFORE
I trigger BEFORE partono prima che PostgreSQL faccia l’operazione principale. Sono utili se vuoi controllare o cambiare i dati proprio prima che vengano salvati. Immagina che sia come controllare il bagaglio prima di salire sull’aereo: se qualcosa non va, puoi modificarlo o bloccarlo del tutto.
Facciamo un esempio di validazione dati prima dell’inserimento. Supponiamo di avere una tabella students dove teniamo info sugli studenti. Vogliamo essere sicuri che l’età dello studente non superi i 100 anni (poco probabile, ovvio).
Creiamo la tabella:
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
age INT NOT NULL
);
Creiamo la funzione per il trigger:
CREATE OR REPLACE FUNCTION validate_age()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.age > 100 THEN
RAISE EXCEPTION 'L\'età dello studente non può essere maggiore di 100 anni!';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Creiamo il trigger:
CREATE TRIGGER before_insert_students
BEFORE INSERT ON students
FOR EACH ROW
EXECUTE FUNCTION validate_age();
Ora, se provi a inserire uno studente con più di 100 anni, PostgreSQL ti darà errore:
INSERT INTO students (name, age) VALUES ('Ivan Ivanov', 120);
-- Errore: L'età dello studente non può essere maggiore di 100 anni!
Controllo fatto!
Trigger AFTER
I trigger AFTER partono dopo che l’operazione principale è andata a buon fine. Sono utili per fare cose che dipendono dal risultato dell’operazione. Tipo logging o creazione di record collegati.
Scenario: abbiamo la tabella students e vogliamo registrare tutte le modifiche in una tabella di log separata.
Creiamo la tabella per i log:
CREATE TABLE students_log (
id SERIAL PRIMARY KEY,
student_id INT,
operation TEXT,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Creiamo la funzione per il trigger:
CREATE OR REPLACE FUNCTION log_student_changes()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO students_log (student_id, operation)
VALUES (NEW.id, TG_OP); -- TG_OP contiene il tipo di operazione: INSERT, UPDATE o DELETE
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Creiamo il trigger:
CREATE TRIGGER after_insert_students
AFTER INSERT ON students
FOR EACH ROW
EXECUTE FUNCTION log_student_changes();
Ora, quando aggiungi uno studente nuovo, PostgreSQL registra l’operazione nel log in automatico:
INSERT INTO students (name, age) VALUES ('Anna Ling', 22);
SELECT * FROM students_log;
-- Risultato:
-- id | student_id | operation | timestamp
-- 1 | 1 | INSERT | 2023-11-15 12:00:00
Trigger INSTEAD OF
I trigger INSTEAD OF scattano al posto dell’operazione. È l’unico tipo di trigger che puoi usare sulle view. Ti danno flessibilità per gestire operazioni che non puoi fare direttamente sulla view.
Scenario: abbiamo due tabelle courses e teachers. Creiamo una view che le unisce e scriviamo un trigger per gestire gli insert tramite la view.
Creiamo le tabelle:
CREATE TABLE courses (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
teacher_id INT NOT NULL
);
CREATE TABLE teachers (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
Creiamo la view:
CREATE VIEW course_details AS
SELECT
courses.id AS course_id,
courses.name AS course_name,
teachers.name AS teacher_name
FROM courses
JOIN teachers ON courses.teacher_id = teachers.id;
Problema: non puoi semplicemente inserire dati nella view, perché aggrega dati da due tabelle. Soluzione: usiamo un trigger INSTEAD OF.
Creiamo la funzione per il trigger:
CREATE OR REPLACE FUNCTION insert_course_details()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO teachers (name) VALUES (NEW.teacher_name) RETURNING id INTO NEW.teacher_id;
INSERT INTO courses (name, teacher_id) VALUES (NEW.course_name, NEW.teacher_id);
RETURN NULL; -- I dati non vengono salvati nella view
END;
$$ LANGUAGE plpgsql;
Creiamo il trigger:
CREATE TRIGGER instead_of_insert_course_details
INSTEAD OF INSERT ON course_details
FOR EACH ROW
EXECUTE FUNCTION insert_course_details();
Ora puoi inserire dati direttamente nella view:
INSERT INTO course_details (course_name, teacher_name)
VALUES ('Matematica', 'Alex Ming');
SELECT * FROM courses;
-- Risultato:
-- id | name | teacher_id
-- 1 | Matematica | 1
SELECT * FROM teachers;
-- Risultato:
-- id | name
-- 1 | Alex Ming
Confronto tra i tipi di trigger
| Tipo di trigger | Quando viene eseguito | Uso principale |
|---|---|---|
BEFORE |
Prima dell’operazione | Validazione, preparazione dati |
AFTER |
Dopo il completamento con successo | Logging, aggiornamento dati collegati |
INSTEAD OF |
Al posto dell’operazione | Gestione operazioni sulle view |
Particolarità e limiti
BEFORE trigger possono modificare i dati prima dell’operazione. Per esempio, puoi formattare i nomi in automatico (tipo metterli in maiuscolo).
AFTER trigger non possono cambiare i dati, perché l’operazione è già finita. Servono solo per azioni successive.
INSTEAD OF trigger si usano solo sulle view. Permettono di implementare logiche complesse di inserimento/modifica dati su più tabelle collegate.
E questo è tutto per oggi! Se BEFORE, AFTER e INSTEAD OF ti sembrano complicati, tranquillo. L’importante è ricordare il principio base e quando usarli. Prova a fare qualche esempio da solo per fissare meglio il concetto.
GO TO FULL VERSION