Os triggers no PostgreSQL se dividem em três categorias principais:
BEFORE— rodam antes da operação principal (tipoINSERT,UPDATEouDELETE). Você pode usar eles pra impedir que a operação aconteça ou modificar os dados antes de salvar.AFTER— rodam depois que a operação principal terminou. Esse tipo é muito usado pra log, criar registros relacionados ou fazer ações que dependem do sucesso da operação.INSTEAD OF— rodam no lugar da operação real. Só dá pra usar em views. Por exemplo, se alguém tentar inserir dados numa view, você pode controlar esse processo com um triggerINSTEAD OF.
Trigger BEFORE
Triggers BEFORE disparam antes do PostgreSQL executar a operação principal. Eles são úteis se você quiser checar ou mudar os dados logo antes de salvar. Imagina que é tipo aquela conferência de bagagem antes de embarcar no avião: se a bagagem não tá certa, dá pra mudar ou até barrar.
Bora ver um exemplo de validação antes de inserir. Suponha que temos uma tabela students onde guardamos info dos estudantes. Queremos garantir que a idade do estudante não passe de 100 anos (meio improvável, né?).
Criando a tabela:
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
age INT NOT NULL
);
Criando a função do trigger:
CREATE OR REPLACE FUNCTION validate_age()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.age > 100 THEN
RAISE EXCEPTION 'A idade do estudante não pode ser maior que 100 anos!';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Criando o trigger:
CREATE TRIGGER before_insert_students
BEFORE INSERT ON students
FOR EACH ROW
EXECUTE FUNCTION validate_age();
Agora, se você tentar inserir um estudante com mais de 100 anos, o PostgreSQL vai dar erro:
INSERT INTO students (name, age) VALUES ('Ivan Ivanov', 120);
-- Erro: A idade do estudante não pode ser maior que 100 anos!
Simples assim pra validar!
Trigger AFTER
Triggers AFTER disparam depois que a operação principal terminou com sucesso. Eles são bons pra fazer coisas que dependem do resultado da operação. Tipo log ou criar registros relacionados.
Cenário: temos a tabela students e queremos registrar todas as mudanças numa tabela de log separada.
Criando a tabela de log:
CREATE TABLE students_log (
id SERIAL PRIMARY KEY,
student_id INT,
operation TEXT,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Criando a função do 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 tem o tipo da operação: INSERT, UPDATE ou DELETE
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Criando o trigger:
CREATE TRIGGER after_insert_students
AFTER INSERT ON students
FOR EACH ROW
EXECUTE FUNCTION log_student_changes();
Agora, quando você adiciona um novo estudante, o PostgreSQL já registra a operação no log:
INSERT INTO students (name, age) VALUES ('Anna Ling', 22);
SELECT * FROM students_log;
-- Resultado:
-- id | student_id | operation | timestamp
-- 1 | 1 | INSERT | 2023-11-15 12:00:00
Trigger INSTEAD OF
Triggers INSTEAD OF disparam no lugar da operação. É o único tipo de trigger que pode ser usado em views. Eles dão flexibilidade pra tratar operações que não dá pra fazer direto na view.
Cenário: temos duas tabelas courses e teachers. Vamos criar uma view que junta as duas e escrever um trigger pra tratar inserts nessa view.
Criando as tabelas:
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
);
Criando a 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: não dá pra inserir direto na view, porque ela junta dados de duas tabelas. Solução: usar trigger INSTEAD OF.
Criando a função do 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; -- Os dados não ficam salvos na view
END;
$$ LANGUAGE plpgsql;
Criando o trigger:
CREATE TRIGGER instead_of_insert_course_details
INSTEAD OF INSERT ON course_details
FOR EACH ROW
EXECUTE FUNCTION insert_course_details();
Agora você pode inserir dados direto na view:
INSERT INTO course_details (course_name, teacher_name)
VALUES ('Matemática', 'Alex Ming');
SELECT * FROM courses;
-- Resultado:
-- id | name | teacher_id
-- 1 | Matemática | 1
SELECT * FROM teachers;
-- Resultado:
-- id | name
-- 1 | Alex Ming
Comparando os tipos de triggers
| Tipo de trigger | Quando executa | Uso principal |
|---|---|---|
BEFORE |
Antes da operação | Validação, preparação de dados |
AFTER |
Depois do sucesso da operação | Log, atualização de dados relacionados |
INSTEAD OF |
No lugar da operação | Tratar operações em views |
Particularidades e limitações
BEFORE triggers podem mudar os dados antes da operação. Por exemplo, dá pra formatar nomes automaticamente (tipo deixar tudo maiúsculo).
AFTER triggers não conseguem mudar os dados, porque a operação já rolou. Eles servem só pra ações depois.
INSTEAD OF triggers só funcionam em views. Eles permitem implementar lógica mais complexa de insert/update em várias tabelas relacionadas.
É isso por hoje! Se BEFORE, AFTER e INSTEAD OF parecerem complicados, relaxa. O importante é lembrar o princípio de cada um e quando usar. Tenta fazer uns exemplos aí pra fixar o conteúdo.
GO TO FULL VERSION