Pular para o conteúdo

Triggers em PL/SQL: Melhores práticas, usos comuns e armadilhas a serem evitadas

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

Quão útil foi este post ?

Clique em uma estrela para classificar o post

nota média 5 / 5. Contagem de votos: 6

Sem votos ! Seja o primeiro a classificar !

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

plugins premium WordPress