CodeGym /Cursos /SQL SELF /Análise dos erros mais comuns na depuração e otimização d...

Análise dos erros mais comuns na depuração e otimização de PL/pgSQL

SQL SELF
Nível 56, Lição 4
Disponível

Hoje, pra fechar essa jornada épica pelo PL/pgSQL, vamos analisar os erros mais comuns que podem te pegar de surpresa enquanto você debuga e otimiza funções e procedimentos. Conhecer esses erros vai te ajudar não só a evitar problemas no futuro, mas também a resolver bugs de forma mais eficiente, caso eles apareçam.

Erros comuns na depuração e otimização

1. Uso errado de variáveis

Um dos erros mais frequentes ao escrever e debugar funções em PL/pgSQL é declarar ou usar variáveis do jeito errado. Por exemplo, se você esquece de definir explicitamente o tipo da variável ou se confunde nos valores passados pelos parâmetros. Bora ver como isso pode aparecer na prática:

CREATE OR REPLACE FUNCTION calculate_discount(order_total NUMERIC)
RETURNS NUMERIC AS $$
DECLARE
    discount_rate NUMERIC;
BEGIN
    -- Opa! Esqueci de inicializar a variável discount_rate
    RETURN order_total * discount_rate;
END;
$$ LANGUAGE plpgsql;

Quando você chamar essa função, vai rolar um erro por causa do uso de NULL nos cálculos, já que a variável discount_rate não foi inicializada.

Como evitar:

  1. Sempre atribua valores padrão pras variáveis quando declarar:
   DECLARE
       discount_rate NUMERIC := 0.1; -- Valor padrão
  1. Cheque o valor das variáveis usando RAISE NOTICE pra garantir que elas têm o valor esperado:
RAISE NOTICE 'Valor de discount_rate: %', discount_rate;

2. Falta de logging de erros

Outro problema comum é não ter um esquema de logging. Se algo dá errado e você não registra o que está rolando na sua função, é tipo procurar um gato preto num quarto escuro, principalmente se você nem sabe se o gato tá lá.

Olha um exemplo de função sem logging:

CREATE OR REPLACE FUNCTION process_order(order_id INT)
RETURNS VOID AS $$
BEGIN
    -- Alguma lógica cabulosa de processamento de pedido
    UPDATE orders SET status = 'processed' WHERE id = order_id;
END;
$$ LANGUAGE plpgsql;

E se o order_id for passado errado? E se não existir esse registro na tabela orders?

Como evitar: Adicione RAISE NOTICE ou RAISE EXCEPTION pra logar os passos críticos:

CREATE OR REPLACE FUNCTION process_order(order_id INT)
RETURNS VOID AS $$
BEGIN
    -- Logando os dados de entrada
    RAISE NOTICE 'Processando pedido com ID %', order_id;

    -- Lógica cabulosa de processamento
    UPDATE orders SET status = 'processed' WHERE id = order_id;

    -- Logando o resultado
    RAISE NOTICE 'Status do pedido atualizado para ID %', order_id;
END;
$$ LANGUAGE plpgsql;

Assim, fica fácil rastrear onde o erro tá rolando, graças às mensagens que aparecem.

3. Ignorar a performance das queries

Esse é um dos maiores vilões de quem mexe com banco de dados. Por exemplo, você escreve uma função que parece certinha, mas roda devagar demais. E uma das principais causas de queries lentas é a falta de índices ou planos de execução ruins.

Exemplo de query lenta:

CREATE OR REPLACE FUNCTION get_large_orders()
RETURNS TABLE(order_id INT, total NUMERIC) AS $$
BEGIN
    RETURN QUERY
    SELECT id, total FROM orders WHERE total > 1000;
END;
$$ LANGUAGE plpgsql;

Se o campo total na tabela orders não tiver índice, a query vai escanear a tabela toda, o que é super ineficiente.

Como evitar:

  1. Use EXPLAIN ANALYZE pra checar se suas queries estão rápidas:
EXPLAIN ANALYZE SELECT id, total FROM orders WHERE total > 1000;
  1. Crie índices nas colunas que você usa bastante:
CREATE INDEX idx_orders_total ON orders(total);

4. Usar o nível errado de isolamento de transação

Quando você executa procedimentos complexos, às vezes rolam erros por não entender direito os níveis de isolamento das transações. Por exemplo, se duas transações tentam atualizar o mesmo registro ao mesmo tempo, pode rolar um deadlock.

Exemplo de possível deadlock:

BEGIN;
UPDATE orders SET status = 'processed' WHERE id = 1;

-- Esperando o lock de outra transação
UPDATE inventory SET stock = stock - 1 WHERE product_id = 100;
COMMIT;

Se outra transação tentar fazer essas operações em ordem diferente, vai dar deadlock.

Como evitar:

  1. Pense bem na ordem das operações e siga sempre a mesma ordem.
  2. Use o nível de isolamento SERIALIZABLE se precisar.

5. Falta de tratamento de erros

Tratar erros não é só boa prática, é essencial pra garantir que seu código seja estável. Por exemplo, no código abaixo não tem nenhum tratamento de erro:

CREATE OR REPLACE FUNCTION add_order(order_id INT)
RETURNS VOID AS $$
BEGIN
    INSERT INTO orders (id, status) VALUES (order_id, 'new');
END;
$$ LANGUAGE plpgsql;

Se o order_id já existir, vai rolar o erro duplicate key value violates unique constraint.

Como evitar: Use blocos de tratamento de exceção:

CREATE OR REPLACE FUNCTION add_order(order_id INT)
RETURNS VOID AS $$
BEGIN
    INSERT INTO orders (id, status) VALUES (order_id, 'new');
EXCEPTION WHEN unique_violation THEN
    RAISE NOTICE 'Pedido com ID % já existe!', order_id;
END;
$$ LANGUAGE plpgsql;

Exemplos de erros e como corrigir

Erro 1: Queries lentas por falta de índice

Situação: Você tem uma query que filtra a tabela por uma coluna, mas essa coluna não tem índice.

Correção: Crie um índice pra essa coluna.

Erro 2: Lógica da função confusa e difícil de debugar

Situação: A função tem lógica demais e não está dividida em subfunções.

Correção: Quebre a função grande em subfunções menores. Isso melhora a leitura e facilita o debug.

Erro 3: Uso errado de RAISE EXCEPTION

Situação: RAISE EXCEPTION é usado pra todo tipo de erro, até os mais de boa.

Correção: Use RAISE NOTICE pra mensagens informativas e só RAISE EXCEPTION pra casos críticos.

RAISE NOTICE 'Tá tudo sob controle — etapa atual da função finalizada.';
RAISE EXCEPTION 'Deu ruim! Dá uma olhada nos parâmetros de entrada.';

Dicas pra evitar erros

  1. Adicione logging: nos pontos críticos da sua função, use RAISE NOTICE pra acompanhar o que tá rolando.
  2. Teste as funções: sempre use dados de teste pra validar funções e procedimentos.
  3. Mantenha o código legível: quebre funções complexas em subfunções e procedimentos menores.
  4. Analise a performance: use EXPLAIN ANALYZE pra garantir que suas queries estão rápidas.
  5. Esteja pronto pra surpresas: sempre coloque blocos de tratamento de exceção pra lidar com erros.
EXCEPTION
    WHEN OTHERS THEN
        RAISE EXCEPTION 'Rolou um erro inesperado: %', SQLERRM;

Assim, você vai conseguir lidar com erros numa boa e ainda evitar que eles apareçam no futuro.

2
Tarefa
SQL SELF, nível 56, lição 4
Bloqueado
Tratamento de erro para valor duplicado
Tratamento de erro para valor duplicado
1
Pesquisa/teste
Otimização de funções, nível 56, lição 4
Indisponível
Otimização de funções
Otimização de funções
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION