
SQL de verdade — o que mudou quando parei de fugir do banco de dados
Durante anos eu tratei o banco de dados como uma caixa preta que guardava dados. Aprender SQL de verdade mudou completamente como eu penso sobre performance e modelagem.
Durante um bom tempo eu escrevi SQL de um jeito específico: o mínimo necessário para buscar o que precisava.
SELECT * FROM tabela WHERE id = ?. Joins simples quando precisava de dados de duas tabelas. ORM quando o ORM dava conta. E quando o ORM não dava conta, eu tentava forçá-lo a dar.
O banco de dados era, na minha cabeça, um lugar onde dados ficavam guardados. Uma caixa preta com interface de consulta.
Esse entendimento funcionou por um tempo. Parou de funcionar quando eu comecei a trabalhar com volumes de dados maiores e com queries que precisavam ser rápidas.
A primeira vez que vi uma query demorar vinte segundos num banco de dados que tinha apenas cinquenta mil registros, fui investigar o que estava acontecendo.
EXPLAIN foi a ferramenta que mudou minha relação com SQL.
EXPLAIN ANALYZE SELECT ... mostra o plano de execução da query. Quantas linhas foram varridas. Quais índices foram usados, ou não usados. Quanto tempo cada operação levou. É como abrir o capô do banco de dados e ver o motor funcionando.
Aquela query de vinte segundos estava fazendo um full scan de cinquenta mil registros porque a coluna do WHERE não tinha índice. Adicionar o índice reduziu o tempo para oitenta milissegundos. A mesma query, os mesmos dados, duzentos e cinquenta vezes mais rápida.
Aprendi a diferença entre o que eu estava pedindo para o banco e como o banco estava executando.
JOIN parece uma operação simples no SQL — une duas tabelas. Por baixo, o banco de dados tem várias estratégias possíveis: nested loop, hash join, merge join. Qual estratégia ele usa depende do tamanho das tabelas, dos índices disponíveis, das estatísticas que ele mantém sobre os dados. Entender isso não é necessário para escrever SQL básico — mas quando uma query fica lenta, é o que você precisa saber para diagnosticar.
Índices são o segundo conceito que muda tudo. Um índice acelera leitura e desacelera escrita, porque o índice precisa ser atualizado em cada INSERT ou UPDATE. Isso significa que ter índice em tudo não é gratuito — você está trocando escrita mais lenta por leitura mais rápida. Entender esse trade-off é saber quando criar um índice e quando não criar.
Window functions foram a revelação que eu não esperava.
Antes de aprender window functions, eu resolvia alguns problemas buscando mais dados do que precisava e processando no código da aplicação. Calcular ranking, médias móveis, diferenças entre linhas consecutivas — tudo isso com SQL básico exige subqueries complexas ou múltiplas queries.
Com ROW_NUMBER(), RANK(), LAG(), LEAD(), SUM() OVER(), essas operações acontecem dentro do banco, mais próximas dos dados, geralmente mais rápidas e com menos tráfego entre a aplicação e o banco.
Modelagem é onde tudo se une.
Como você estrutura as tabelas determina quais queries são naturais e quais são torturadas. Uma modelagem que não reflete como os dados são acessados vai gerar queries complexas para operações simples.
Normalização — o processo de organizar dados para reduzir redundância — tem trade-offs. Tabelas normalizadas requerem mais joins. Desnormalização proposital, adicionar redundância estrategicamente para acelerar leituras frequentes, é legítima em sistemas que priorizam performance de leitura sobre consistência.
Não existe modelagem certa em abstrato. Existe modelagem que serve bem os padrões de acesso do sistema.
Essa semana: pegue a query mais lenta do sistema que você trabalha e rode EXPLAIN ANALYZE nela. Não precisa otimizar agora — só entenda o que o banco está fazendo para executá-la. Esse exercício revela mais sobre como bancos de dados funcionam do que qualquer tutorial.