Triggers em PL/SQL: Melhores práticas, usos comuns, e armadilhas a serem evitadas
Triggers, no contexto do Oracle e PL/SQL, são procedimentos armazenados que são executados automaticamente em resposta a determinados eventos em uma tabela ou vista em um banco de dados. Estes podem ser muito úteis, mas também podem ser fontes de problemas se não forem bem projetados.
Usos Comuns de Triggers
Auditoria de Dados
Triggers podem ser usados para registrar as mudanças em registros.
Exemplo: Suponha uma tabela orders
e uma tabela orders_audit
para monitorar mudanças.
-- Tabela original
CREATE TABLE orders (order_id NUMBER, order_detail VARCHAR2(255));
-- Tabela de auditoria
CREATE TABLE orders_audit (order_id NUMBER, order_detail VARCHAR2(255), audit_timestamp TIMESTAMP, action VARCHAR2(10));
Trigger para auditar inserções
CREATE OR REPLACE TRIGGER trg_audit_orders
AFTER INSERT ON orders
FOR EACH ROW
BEGIN
INSERT INTO orders_audit(order_id, order_detail, audit_timestamp, action)
VALUES (:NEW.order_id, :NEW.order_detail, SYSTIMESTAMP, 'INSERT');
END;
/
Simulação
INSERT INTO orders VALUES (1, 'First Order');
Saída da tabela orders_audit
1, 'First Order', '25-AUG-2023 10:00:00', 'INSERT'
Validação de Dados
Triggers podem verificar se os dados cumprem determinados critérios.
Exemplo: Evitar que o detalhe do pedido na tabela orders
seja NULL.
CREATE OR REPLACE TRIGGER trg_validate_order_detail
BEFORE INSERT OR UPDATE ON orders
FOR EACH ROW
WHEN (NEW.order_detail IS NULL)
BEGIN
RAISE_APPLICATION_ERROR(-20001, 'Order detail cannot be NULL.');
END;
/
Simulação
INSERT INTO orders(order_id) VALUES (2);
Saída
Error: ORA-20001: Order detail cannot be NULL.
Modificação Automática de Dados
Definir automaticamente a data e hora da criação.
Exemplo: Adicionar uma coluna creation_date
à tabela orders
e definir o valor ao inserir.
ALTER TABLE orders ADD (creation_date TIMESTAMP);
CREATE OR REPLACE TRIGGER trg_set_creation_date
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
:NEW.creation_date := SYSTIMESTAMP;
END;
/
Simulação
INSERT INTO orders(order_id, order_detail) VALUES (3, 'Another Order');
Saída na tabela orders
3, 'Another Order', '25-AUG-2023 10:05:00'
Melhores Práticas
Mantenha os Triggers Pequenos
Exemplo: Para um trigger de auditoria e validação, separe-os em dois triggers distintos para clareza e manutenibilidade.
-- Trigger para auditoria
CREATE OR REPLACE TRIGGER trg_audit_orders
AFTER INSERT ON orders
FOR EACH ROW
BEGIN
INSERT INTO orders_audit(order_id, order_detail, audit_timestamp, action)
VALUES (:NEW.order_id, :NEW.order_detail, SYSTIMESTAMP, 'INSERT');
END;
/
-- Trigger para validação
CREATE OR REPLACE TRIGGER trg_validate_order_detail
BEFORE INSERT OR UPDATE ON orders
FOR EACH ROW
WHEN (NEW.order_detail IS NULL)
BEGIN
RAISE_APPLICATION_ERROR(-20001, 'Order detail cannot be NULL.');
END;
/
Evite Mutação de Tabela
Exemplo: Usar um trigger que tenta modificar a mesma tabela a que pertence.
CREATE OR REPLACE TRIGGER trg_mutate_order
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
DELETE FROM orders WHERE order_id = :NEW.order_id;
END;
/
Simulação
INSERT INTO orders VALUES (4, 'Mutation Test Order');
Saída
Error: ORA-04091: table ORDERS is mutating, trigger/function may not see it
Seja Cauteloso com Triggers de CASCADE
Exemplo: Um trigger que tenta excluir registros relacionados em outra tabela automaticamente. Esta abordagem pode causar deleções acidentais.
-- Suponha que temos uma tabela 'order_items' relacionada a 'orders'
CREATE TABLE order_items (item_id NUMBER, order_id NUMBER, product_name VARCHAR2(255));
CREATE OR REPLACE TRIGGER trg_cascade_delete
AFTER DELETE ON orders
FOR EACH ROW
BEGIN
DELETE FROM order_items WHERE order_id = :OLD.order_id;
END;
/
Armadilhas a serem Evitadas
Desempenho
Exemplo: Usar um trigger para verificar a disponibilidade de um produto ao inserir um pedido pode afetar o desempenho se a tabela de produtos for muito grande.
CREATE OR REPLACE TRIGGER trg_check_product_availability
BEFORE INSERT ON orders
FOR EACH ROW
DECLARE
v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count FROM products WHERE product_id = :NEW.product_id AND availability = 'YES';
IF v_count = 0 THEN
RAISE_APPLICATION_ERROR(-20002, 'Product not available.');
END IF;
END;
/
Debugging
Exemplo: Imagine um trigger que insere em uma tabela de logs sempre que um erro ocorre. Este trigger pode obscurecer a causa real dos problemas.
CREATE OR REPLACE TRIGGER trg_error_logging
AFTER SERVERERROR
BEGIN
INSERT INTO error_logs(timestamp, message) VALUES (SYSTIMESTAMP, DBMS_UTILITY.FORMAT_ERROR_STACK);
END;
/
Ordem de Execução
Exemplo: Se tivermos dois triggers, trg_a
e trg_b
, que são ativados após uma inserção na mesma tabela, não há garantia de qual trigger será executado primeiro.
CREATE OR REPLACE TRIGGER trg_a
AFTER INSERT ON orders
BEGIN
DBMS_OUTPUT.PUT_LINE('Trigger A executed');
END;
/
CREATE OR REPLACE TRIGGER trg_b
AFTER INSERT ON orders
BEGIN
DBMS_OUTPUT.PUT_LINE('Trigger B executed');
END;
/
Simulação
INSERT INTO orders(order_id, order_detail) VALUES (5, 'Order Execution Test');
Saída
Trigger A executed
Trigger B executed
ou
Trigger B executed
Trigger A executed
Conclusão
Triggers são ferramentas poderosas no arsenal PL/SQL. Usados corretamente, podem simplificar a manutenção de dados, melhorar a integridade e automatizar tarefas. No entanto, como qualquer ferramenta poderosa, eles devem ser usados com cuidado e entendimento.
Espero que este artigo completo forneça uma compreensão abrangente sobre os triggers em PL/SQL, suas melhores práticas, usos comuns e armadilhas!
Abs
Referências