← Voltar às notas

PERFORMANCE · Publicado em out/2025

O banco quase sempre conta uma parte da história

Um relatório crítico levava 45 segundos. O execution plan mostrava table scan em 3 tabelas. O banco dava as pistas — o trabalho era montar o caminho completo.

O cenário

O relatório era usado pelo financeiro todos os dias, primeira hora. Cruzava dados de 3 tabelas com 500k+ registros cada. Com o crescimento de dados, o tempo subiu de "aceitável" para 45 segundos — o suficiente para o usuário achar que o sistema travou.

A investigação

O primeiro passo foi olhar o execution plan. Table scans nas 3 tabelas principais, nested loops onde deveria haver hash joins, e uma procedure cheia de cursores processando linha por linha o que poderia ser uma operação de conjunto.

O problema não era um — eram três camadas:

  1. Índices ausentes — as colunas de JOIN e WHERE não tinham índices compostos
  2. Cursores — processamento row-by-row em vez de set-based
  3. Sem paginação — o relatório carregava todos os registros de uma vez, mesmo que o usuário visse apenas 50 por página

A solução

Criei índices compostos cobrindo as colunas de JOIN + filtro mais frequentes. Reescrevi a procedure inteira: eliminei os cursores, substituí por CTEs e operações de conjunto. Adicionei paginação server-side com OFFSET/FETCH para entregar apenas a página solicitada.

O resultado

Tempo final: 2 segundos. A diferença não veio de uma única mudança, mas da combinação. O índice sozinho já teria ajudado, mas sem eliminar os cursores e sem paginar, o ganho seria parcial.

O aprendizado

O execution plan diz onde dói, mas não diz tudo. Ele mostra que tem table scan, mas não mostra que a procedure está iterando linha por linha. Otimizar banco é seguir pistas em camadas — e cada camada que você resolve revela a próxima.