Dev Tools

Otimização de Queries SQL: Técnicas de Indentação e Performance

Aprenda como queries mal formatadas escondem graves gargalos no PostgreSQL e MySQL. Guia prático de legibilidade, análise de EXPLAIN e uso de índices.

04 de junho de 20269 min de leituraDevThru

Na correria do dia a dia do desenvolvimento full stack, é muito comum darmos foco quase exclusivo às regras de negócio e deixarmos as consultas ao banco de dados em segundo plano. O resultado? Queries gigantescas, com centenas de linhas coladas em strings do ORM, sem nenhuma formatação ou padrão.

Mas o problema de uma consulta SQL desorganizada vai muito além da estética: código desestruturado esconde redundâncias e graves gargalos de performance. Joins duplicados, subqueries aninhadas desnecessárias e filtros ineficientes passam facilmente despercebidos em revisões de código de arquivos mal formatados.

Neste guia, vamos analisar como a formatação adequada e técnicas modernas de estruturação de SQL ajudam você a identificar gargalos, como desvendar o plano de execução do banco de dados com a instrução EXPLAIN, e como manter um padrão limpo e eficiente no seu time.

1. Como a Má Formatação Oculta Gargalos de Desempenho

Considere uma consulta SQL complexa de 150 linhas sem recuos, espaçamentos ou quebras de linha lógicas. Encontrar um erro de lógica nessa query é como procurar uma agulha no palheiro. Além do desperdício de tempo na leitura, a falta de formatação esconde armadilhas comuns:

  • Subqueries Repetitivas: O mesmo sub-SELECT é executado múltiplas vezes no SELECT principal em vez de ser resolvido uma única vez.
  • Joins Duplicados: Junções com a mesma tabela de metadados em partes distintas do código, forçando o banco a varrer os mesmos índices repetidamente.
  • Filtros Incoerentes: Condições na cláusula WHERE que anulam filtros anteriores ou que inviabilizam o uso de índices existentes devido à ordem incorreta das colunas.

A legibilidade é a primeira linha de defesa contra a ineficiência de consultas. Um SQL limpo permite que qualquer desenvolvedor do time bata o olho e perceba se a lógica está realizando operações redundantes.

2. Legibilidade na Prática: CTEs (Common Table Expressions) vs Subqueries

Subqueries aninhadas (uma consulta dentro do FROM ou do WHERE de outra) são extremamente difíceis de ler porque exigem uma interpretação "de dentro para fora". Se você tem múltiplos níveis de aninhamento, a query torna-se ilegível.

A melhor prática moderna para bancos de dados relacionais como PostgreSQL e MySQL (a partir da versão 8.0) é o uso de CTEs (Common Table Expressions) por meio da cláusula WITH. As CTEs funcionam como "variáveis temporárias" que permitem ler o SQL de cima para baixo, de forma sequencial e puramente declarativa.

Veja a diferença de legibilidade:

Abordagem Confusa (Subquery Aninhada)

SELECT p.nome, vendas_totais.total
FROM parceiros p
INNER JOIN (
  SELECT pedido.parceiro_id, SUM(pedido.valor_total) as total
  FROM pedidos pedido
  WHERE pedido.status = 'pago' AND pedido.criado_em >= '2026-01-01'
  GROUP BY pedido.parceiro_id
) as vendas_totais ON vendas_totais.parceiro_id = p.id
ORDER BY vendas_totais.total DESC;

Abordagem Limpa (Common Table Expression - CTE)

WITH vendas_filtradas AS (
  SELECT 
    parceiro_id, 
    SUM(valor_total) AS total
  FROM pedidos
  WHERE status = 'pago' 
    AND criado_em >= '2026-01-01'
  GROUP BY parceiro_id
)

SELECT 
  p.nome, 
  vf.total
FROM parceiros p
INNER JOIN vendas_filtradas vf ON vf.parceiro_id = p.id
ORDER BY vf.total DESC;

A segunda query é infinitamente mais simples de manter e revisar, pois isola a agregação dos dados da exibição final. No PostgreSQL moderno, o planejador de consultas otimiza as CTEs perfeitamente, garantindo que não haja perda de performance em relação às subqueries tradicionais.

3. Desvendando a Caixa Preta com o EXPLAIN

Depois de estruturar a sua query de forma legível, o próximo passo é analisar como o banco de dados realmente processa as informações. Para isso, utilizamos a instrução EXPLAIN antes da consulta (ou EXPLAIN ANALYZE para executar a query e exibir os tempos reais de execução).

O retorno do EXPLAIN detalha o plano de execução, que é o mapa de decisões tomado pelo otimizador do banco de dados. Os dois principais pontos de atenção que você deve procurar no output são:

Operação no Plano Impacto na Performance Descrição
Seq Scan (ou Table Scan) Alta Ineficiência 🔴 O banco lê a tabela inteira, linha por linha, do disco. Aceitável apenas em tabelas minúsculas.
Index Scan Alta Eficiência 🟢 O banco localiza os registros correspondentes utilizando a estrutura em árvore do índice.
Index Only Scan Excelente Performance 🚀 Toda a informação solicitada está no próprio índice. O banco de dados nem precisa acessar a tabela principal.

Veja um exemplo simplificado da saída do EXPLAIN ANALYZE no PostgreSQL:

EXPLAIN ANALYZE SELECT email FROM usuarios WHERE id = 12345;

Index Scan using usuarios_pkey on usuarios  (cost=0.29..8.30 rows=1 width=32) (actual time=0.015..0.016 rows=1 loops=1)
  Index Cond: (id = 12345)
Planning Time: 0.082 ms
Execution Time: 0.035 ms

Ao analisar a saída, se você visualizar um Seq Scan em uma tabela com milhões de registros, significa que está faltando um índice adequado para a condição do seu WHERE ou JOIN.

💡 Dica: Planos de execução complexos podem retornar milhares de linhas em formato JSON. Se você estiver trabalhando com queries muito ramificadas no PostgreSQL ou MySQL, gere e analise o plano de execução em formato JSON estruturado e use um formatador para facilitar a leitura das ramificações.

4. Por que o Banco Ignora seus Índices?

Criar um índice na coluna não garante que o banco de dados irá utilizá-lo. Existem cenários clássicos de codificação que "quebram" o uso de índices e derrubam o desempenho da sua aplicação em produção:

A. Funções no WHERE

Ao aplicar uma função em uma coluna indexada, o banco de dados precisa computar o resultado da função para todas as linhas da tabela antes de comparar, invalidando o índice:

-- Ineficiente (Ignora o índice na coluna email):
SELECT id FROM usuarios WHERE LOWER(email) = 'contato@empresa.com';

-- Otimizado (Mantém o valor da coluna intacto):
SELECT id FROM usuarios WHERE email = 'contato@empresa.com';

Se você realmente precisa de buscas case-insensitive, utilize um índice funcional (Expression Index) no PostgreSQL ou salve os e-mails sempre em minúsculo na inserção.

B. Busca de Texto Ineficiente (LIKE '%termo')

Índices do tipo B-Tree tradicionais são lidos da esquerda para a direita. Usar um caractere coringa (%) no início do termo impede que o otimizador use o índice:

-- Ineficiente (Força um Seq Scan):
SELECT id FROM produtos WHERE codigo LIKE '%1234';

-- Otimizado (Usa o índice):
SELECT id FROM produtos WHERE codigo LIKE '1234%';

5. Automatização e Padronização em Times Distribuídos

Manter o código SQL limpo e padronizado em repositórios com muitos desenvolvedores trabalhando em paralelo é difícil se for feito apenas de forma manual. Revisar cada pull request caçando SQLs mal formatados consome energia preciosa do time.

A melhor abordagem de engenharia de software para resolver isso envolve:

  1. Configurar Linters de SQL: Ferramentas como o SQLFluff integradas ao pipeline de CI/CD para bloquear commits com formatações fora de padrão.
  2. Utilizar Formatadores Online Rápidos: Permitir que os desenvolvedores formatem suas consultas localmente durante a etapa de debug antes de submeter o código.
  3. Criar Massa de Testes de Carga: Popular as bases locais de desenvolvimento e homologação com dados volumosos e realistas para que consultas lentas gritem nos logs de desenvolvimento antes de chegarem aos clientes finais.

No DevThru, disponibilizamos utilitários focados para o seu fluxo de desenvolvimento local:

Conclusão

Otimizar banco de dados não é uma tarefa reservada apenas para DBAs seniores. Como desenvolvedor full stack, escrever queries legíveis e organizadas, saber interpretar o output de um EXPLAIN e entender o comportamento de índices básicos garante que seu sistema escale de maneira sustentável e sem custos desnecessários de infraestrutura.

🛠️ Experimente na prática

Use nossas ferramentas online gratuitas — sem cadastro, direto no navegador.

SQLPostgreSQLMySQLbanco de dadosperformancequeryEXPLAINdesenvolvimento