Triggers e Eventos

Triggers e Eventos

Analogia do Mundo Real

Imagine um sistema de casa inteligente:

Conceitos Fundamentais

O que é uma Trigger

Uma Trigger é um objeto de banco de dados associado a uma tabela que é executado automaticamente quando um evento específico ocorre nessa tabela:

Momento da Trigger: BEFORE vs AFTER

Momento Descrição Uso Típico
BEFORE Dispara antes da execução da operação Validação de dados, modificação automática de valores
AFTER Dispara após a execução da operação Logs de auditoria, atualizações em cascata

Tipos de Eventos da Trigger

Referências NEW e OLD

Você pode acessar os dados sendo operados dentro de uma trigger:

Evento NEW OLD
INSERT A linha recém-inserida Não disponível
UPDATE O valor após a atualização O valor antes da atualização
DELETE Não disponível A linha excluída
SQL
-- Exemplo SQLite: acessando NEW e OLD
-- NEW.nome_coluna: referencia os novos dados
-- OLD.nome_coluna: referencia os dados antigos

Casos de Uso Típicos de Triggers

  1. Logs de auditoria: Registrar quem modificou quais dados e quando
  2. Atualizações em cascata: Atualizar automaticamente dados em tabelas relacionadas
  3. Validação de dados: Realizar validação complexa de regras de negócios antes de dados serem gravados
  4. Cálculo automático: Calcular automaticamente valores de campos derivados
  5. Manutenção de dados redundantes: Sincronizar atualizações em tabelas de resumo ou cache

Conhecimento Estendido: Agendador de Eventos em Outros Bancos de Dados

⚠️ Nota: SQLite não suporta o agendador de eventos. O conteúdo a seguir é conhecimento estendido mostrando a sintaxe do agendador de eventos do MySQL como referência.

O agendador de eventos permite executar tarefas automaticamente em um cronograma de tempo:

SQL
-- Exemplo de agendador de eventos MySQL
-- Habilitar o agendador de eventos
SET GLOBAL event_scheduler = ON;

-- Criar um evento agendado: limpar dados expirados diariamente à meia-noite
CREATE EVENT cleanup_expired_orders
ON SCHEDULE EVERY 1 DAY
STARTS '2024-01-01 00:00:00'
DO
    DELETE FROM orders 
    WHERE status = 'expired' 
    AND created_at < DATE_SUB(NOW(), INTERVAL 90 DAY);

-- Criar um evento único
CREATE EVENT one_time_report
ON SCHEDULE AT '2024-12-31 23:59:59'
DO
    INSERT INTO annual_report (year, total_sales)
    SELECT YEAR(NOW()), SUM(amount) FROM orders WHERE YEAR(created_at) = YEAR(NOW());

-- Ver todos os eventos
SHOW EVENTS;

-- Desabilitar um evento
ALTER EVENT cleanup_expired_orders DISABLE;

-- Excluir um evento
DROP EVENT IF EXISTS cleanup_expired_orders;

Sintaxe Básica

Sintaxe de Trigger SQLite

SQLite suporta triggers com a seguinte sintaxe:

SQL
-- Sintaxe básica de criação de trigger
CREATE TRIGGER nome_trigger
{BEFORE | AFTER} {INSERT | UPDATE | DELETE} ON nome_tabela
[FOR EACH ROW]
[WHEN condição]
BEGIN
    -- Lógica da trigger
END;

Sintaxe de Trigger MySQL

SQL
-- Sintaxe de trigger MySQL
DELIMITER //
CREATE TRIGGER nome_trigger
{BEFORE | AFTER} {INSERT | UPDATE | DELETE} ON nome_tabela
FOR EACH ROW
BEGIN
    -- Lógica da trigger
    -- Use NEW e OLD para referenciar dados
END //
DELIMITER ;

Sintaxe de Trigger PostgreSQL

SQL
-- PostgreSQL requer criar uma função de trigger primeiro
CREATE OR REPLACE FUNCTION nome_funcao_trigger()
RETURNS TRIGGER AS $$
BEGIN
    -- Lógica da trigger
    RETURN NEW; -- ou RETURN OLD
END;
$$ LANGUAGE plpgsql;

-- Depois criar a trigger
CREATE TRIGGER nome_trigger
{BEFORE | AFTER} {INSERT | UPDATE | DELETE} ON nome_tabela
FOR EACH ROW
EXECUTE FUNCTION nome_funcao_trigger();
💡 Dica: Triggers adicionam complexidade às operações do banco de dados. Use-as com cautela e evite criar lógica de trigger excessivamente complexa.

Exemplos

Exemplo: Criando uma Trigger de Log de Auditoria (Dificuldade ⭐⭐)

Crie uma trigger que registre automaticamente o histórico de modificações de salários de funcionários.

SQL
-- Versão SQLite
-- Primeiro criar a tabela de log de auditoria
CREATE TABLE IF NOT EXISTS salary_audit_log (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    employee_id INTEGER,
    old_salary REAL,
    new_salary REAL,
    changed_by TEXT,
    changed_at TEXT DEFAULT (datetime('now', 'localtime')),
    action TEXT
);

-- Criar trigger: registrar atualizações de salário
CREATE TRIGGER audit_salary_update
AFTER UPDATE ON employees
FOR EACH ROW
WHEN OLD.salary != NEW.salary
BEGIN
    INSERT INTO salary_audit_log (
        employee_id, 
        old_salary, 
        new_salary, 
        changed_by, 
        action
    )
    VALUES (
        NEW.id,
        OLD.salary,
        NEW.salary,
        'system',  -- Na prática, você pode usar o usuário atual
        'UPDATE'
    );
END;

-- Testar a trigger
UPDATE employees SET salary = 12000 WHERE id = 1;

-- Ver o log de auditoria
SELECT * FROM salary_audit_log;
▶ Experimente

Exemplo: Trigger BEFORE INSERT para Preenchimento Automático de Dados (Dificuldade ⭐⭐)

Crie uma trigger que defina automaticamente a data do pedido e o status inicial ao inserir um pedido.

SQL
-- Criar tabela de pedidos
CREATE TABLE IF NOT EXISTS orders_new (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    customer_name TEXT NOT NULL,
    product_id INTEGER,
    quantity INTEGER DEFAULT 1,
    order_date TEXT,
    status TEXT DEFAULT 'pending',
    created_at TEXT
);

-- Criar trigger BEFORE INSERT
CREATE TRIGGER set_order_defaults
BEFORE INSERT ON orders_new
FOR EACH ROW
BEGIN
    -- Definir automaticamente a data do pedido para a data atual
    SET NEW.order_date = COALESCE(NEW.order_date, date('now', 'localtime'));
    -- Definir automaticamente o horário de criação
    SET NEW.created_at = COALESCE(NEW.created_at, datetime('now', 'localtime'));
    -- Se a quantidade for nula ou 0, padronizar para 1
    SET NEW.quantity = CASE WHEN NEW.quantity IS NULL OR NEW.quantity <= 0 THEN 1 ELSE NEW.quantity END;
END;

-- Testar: inserir sem especificar order_date e status
INSERT INTO orders_new (customer_name, product_id, quantity) VALUES ('John Doe', 1, 3);

-- Verificar o resultado preenchido automaticamente
SELECT * FROM orders_new;
▶ Experimente

Saída Esperada:

TEXT
id  customer_name  product_id  quantity  order_date   status   created_at
1   John Doe       1           3         2026-06-28   pending  2026-06-28 10:30:00
💡 Limitação do SQLite: A sintaxe de trigger do SQLite difere do MySQL/PostgreSQL. MySQL suporta SET NEW.coluna = valor, enquanto PostgreSQL requer usar NEW.coluna := valor em triggers BEFORE para modificar novos valores.

Cenários de Aplicação

Cenário 1: Atualização Automática de uma Tabela de Resumo de Pedidos (Dificuldade ⭐⭐⭐)

SQL
-- Versão SQLite
-- Criar tabela de resumo de pedidos
CREATE TABLE IF NOT EXISTS order_summary (
    customer_id INTEGER PRIMARY KEY,
    total_orders INTEGER DEFAULT 0,
    total_amount REAL DEFAULT 0,
    last_order_date TEXT
);

-- Criar trigger: atualizar resumo automaticamente na inserção de pedido
CREATE TRIGGER update_order_summary_insert
AFTER INSERT ON orders
FOR EACH ROW
BEGIN
    INSERT OR REPLACE INTO order_summary (customer_id, total_orders, total_amount, last_order_date)
    SELECT 
        NEW.customer_id,
        COALESCE(os.total_orders, 0) + 1,
        COALESCE(os.total_amount, 0) + NEW.amount,
        NEW.order_date
    FROM (SELECT 1) AS dummy
    LEFT JOIN order_summary os ON os.customer_id = NEW.customer_id;
END;

-- Criar trigger: atualizar resumo automaticamente na exclusão de pedido
CREATE TRIGGER update_order_summary_delete
AFTER DELETE ON orders
FOR EACH ROW
BEGIN
    UPDATE order_summary
    SET 
        total_orders = total_orders - 1,
        total_amount = total_amount - OLD.amount
    WHERE customer_id = OLD.customer_id;
    
    -- Se a contagem de pedidos chegar a 0, excluir o registro de resumo
    DELETE FROM order_summary 
    WHERE customer_id = OLD.customer_id AND total_orders <= 0;
END;

Cenário 2: Validação de Dados e Preenchimento Automático (Dificuldade ⭐⭐⭐)

SQL
-- Versão SQLite: cálculo automático do total do pedido
CREATE TRIGGER calculate_order_total
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
    -- Definir automaticamente o horário de criação
    SELECT NEW.created_at IS NULL THEN
        SET NEW.created_at = datetime('now', 'localtime');
    END IF;
    
    -- Validar que o valor deve ser positivo
    IF NEW.amount < 0 THEN
        SELECT RAISE(ABORT, 'O valor do pedido não pode ser negativo');
    END IF;
END;

❓ Perguntas Frequentes

P: Triggers afetam a performance do banco de dados? R: Sim. Cada ativação de trigger executa operações SQL adicionais. Triggers muitas vezes ou lógica complexa podem degradar a performance de escrita. Mantenha a lógica das triggers simples e evite operações demoradas dentro delas.

P: Uma tabela pode ter múltiplas triggers? R: SQLite permite apenas uma trigger por evento e momento. MySQL e PostgreSQL permitem múltiplas triggers e permitem especificar a ordem de execução com FOLLOWS/PRECEDES.

P: Triggers podem acessar outras tabelas? R: Sim. Triggers podem consultar e modificar dados em outras tabelas, mas tenha cuidado para evitar triggers circulares e deadlocks.

P: Como faço para depurar uma trigger? R: Você pode inserir dados de teste temporários em uma tabela de log dentro da trigger para observar o fluxo de execução. MySQL pode usar a instrução SIGNAL para lançar mensagens de erro personalizadas.

📖 Resumo

Nesta lição aprendemos:

📝 Exercícios

  1. Exercício Básico: Crie uma trigger que faça backup automaticamente das informações de um funcionário para uma tabela employees_backup quando o funcionário for excluído.

  2. Exercício Intermediário: Crie um sistema de triggers que implemente o seguinte:

    • Quando um novo pedido é inserido, reduzir automaticamente o estoque do produto
    • Quando o estoque for insuficiente, prevenir a inserção do pedido e mostrar um erro
  3. Questão Reflexiva: Tanto triggers quanto código de aplicação podem implementar lógica de negócios. Para quais cenários cada um é mais adequado? Como você escolhe?


Próxima Lição → 24-practice-advanced.md

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%