Legibilidade em SQL é a capacidade de outra pessoa (ou você mesmo daqui a algumas semanas) entender rapidamente o que a query faz, quais regras de negócio ela aplica e onde ajustar algo sem quebrar o resultado. Em análises do dia a dia, legibilidade não é “estética”: ela reduz erros, facilita revisão por pares, acelera manutenção e diminui o risco de interpretações erradas em relatórios. Boas práticas de legibilidade se apoiam em quatro pilares: alias consistentes, formatação previsível, nomes claros e estrutura de query em blocos.
Princípios de legibilidade que guiam todas as decisões
1) “Leitura em blocos” antes de “leitura linha a linha”
Uma query legível permite que alguém entenda o objetivo olhando primeiro para a estrutura: quais tabelas entram, quais filtros são aplicados, como os campos são calculados e como o resultado é organizado. Isso pede blocos bem separados (SELECT, FROM/JOIN, WHERE, GROUP BY, HAVING, QUALIFY/ORDER BY) e uma ordem consistente de colunas e condições.
2) Consistência é mais importante do que preferência pessoal
Não existe um único estilo “correto”, mas existe estilo inconsistente. Se você escolhe usar palavras-chave em maiúsculas, mantenha em maiúsculas. Se você escolhe prefixar alias com abreviações, mantenha o padrão. Consistência reduz esforço cognitivo.
3) Evite “surpresas” e “mágica”
Legibilidade também é previsibilidade. Evite abreviações obscuras, números mágicos sem contexto, colunas com nomes ambíguos e cálculos escondidos em expressões gigantes. Prefira decompor em etapas e nomear cada etapa de forma descritiva.
Boas práticas de alias: clareza e economia sem perder contexto
Alias de tabela: curtos, estáveis e sem ambiguidade
Alias de tabela devem reduzir repetição sem virar criptografia. Um padrão comum é usar 1 a 3 letras que remetem ao nome da tabela, evitando colisões. Exemplo: orders vira o ou ord, customers vira c ou cus. Em queries com muitas tabelas, alias de 3 letras costuma ser mais legível do que 1 letra.
Continue em nosso aplicativo
Você poderá ouvir o audiobook com a tela desligada, ganhar gratuitamente o certificado deste curso e ainda ter acesso a outros 5.000 cursos online gratuitos.
ou continue lendo abaixo...Baixar o aplicativo
- Evite alias genéricos como
a,b,t1,t2quando houver mais de duas tabelas. - Evite alias que conflitam com nomes de colunas (por exemplo, alias
idpara uma tabela). - Evite mudar o alias da mesma tabela entre queries do mesmo projeto (ex.:
customersàs vezesc, às vezescust), a menos que haja motivo.
Alias de coluna: nomeie o significado, não o cálculo
Alias de coluna devem refletir o que o campo representa no contexto do negócio. Em vez de sum_value ou calc1, prefira revenue_total, orders_count, avg_ticket, is_active_customer. O leitor não precisa saber que foi um SUM; ele precisa saber o que aquilo significa.
Também é importante padronizar idioma e estilo: se você usa snake_case em inglês, mantenha. Se o time usa português, mantenha em português. Misturar idiomas tende a gerar nomes inconsistentes e difíceis de buscar.
Quando usar alias explícito mesmo sem necessidade
Alguns bancos permitem omitir AS, mas usar AS melhora a leitura, principalmente em colunas calculadas. Além disso, alias explícito ajuda a evitar confusão quando há funções e expressões longas.
SELECT o.customer_id AS customer_id, o.order_id AS order_id, o.order_date AS order_dateFROM orders AS o;Mesmo que pareça redundante, esse padrão facilita revisões e diffs em controle de versão, porque mudanças ficam mais evidentes.
Não use alias “enganosos”
Um erro comum é dar um alias que sugere uma regra de negócio diferente do que o cálculo faz. Por exemplo, chamar de active_customers uma contagem que na verdade conta clientes com pedido nos últimos 365 dias, quando o negócio define “ativo” como 90 dias. Se a definição é específica, reflita isso no nome: customers_with_orders_last_365d ou customers_active_365d.
Formatação: transforme a query em um documento fácil de escanear
Palavras-chave em maiúsculas e uma cláusula por bloco
Um padrão muito usado é palavras-chave em maiúsculas e cada cláusula começando em uma nova linha. Isso cria “marcos visuais” que permitem localizar rapidamente onde mexer.
SELECT c.customer_id, c.customer_name, o.order_id, o.order_dateFROM customers AS cINNER JOIN orders AS o ON o.customer_id = c.customer_idWHERE o.order_date >= DATE '2025-01-01'ORDER BY o.order_date DESC;Indentação consistente para JOIN e condições
Indentação é mais do que estética: ela mostra hierarquia. Em JOINs, alinhe o ON e quebre condições em linhas separadas quando houver mais de uma. Em filtros, use uma condição por linha para facilitar adicionar/remover regras.
SELECT o.order_id, o.customer_id, o.order_date, o.statusFROM orders AS oLEFT JOIN order_payments AS op ON op.order_id = o.order_id AND op.is_refunded = 0WHERE o.status IN ('paid', 'shipped') AND o.order_date >= DATE '2025-01-01' AND o.order_date < DATE '2026-01-01';Note que a condição op.is_refunded = 0 ficou no ON porque faz parte do relacionamento e evita transformar o LEFT JOIN em INNER JOIN por acidente. Mesmo quando você já domina esse comportamento, a formatação deixa a intenção clara.
Alinhamento vertical: use com moderação
Alinhar colunas e AS pode melhorar leitura, mas pode piorar diffs quando alguém adiciona um campo no meio. Uma prática equilibrada é alinhar apenas em blocos pequenos (por exemplo, no SELECT final) e evitar alinhamento obsessivo em todas as linhas.
Evite SELECT *
Além de riscos de performance e de mudanças silenciosas no schema, SELECT * prejudica legibilidade: ninguém sabe quais colunas são realmente usadas. Prefira listar colunas e ordenar de forma lógica (identificadores, datas, dimensões, métricas).
Comentários úteis e curtos
Comentários devem explicar “por quê” e “o que significa”, não repetir o óbvio. Bons usos: justificar uma regra de negócio, explicar uma exceção, indicar fonte de uma definição. Evite comentários que descrevem a linha literalmente.
SELECT o.order_id, o.order_date, o.total_amount, CASE WHEN o.status = 'canceled' THEN 1 ELSE 0 END AS is_canceled -- status 'canceled' inclui cancelamentos manuais e automáticos; não usar 'refund' aquiFROM orders AS o;Nomes: padrões que evitam ambiguidade e retrabalho
Padronize estilo: snake_case e sem acentos
Para nomes de colunas derivadas e aliases, snake_case tende a ser o mais compatível entre bancos e ferramentas. Evite acentos e caracteres especiais para reduzir problemas de portabilidade e de integrações.
Use prefixos/sufixos semânticos
Alguns padrões ajudam a entender o tipo do campo rapidamente:
is_para booleanos:is_active,is_refunded.has_para presença:has_email,has_orders._atpara timestamps:created_at,paid_at._datepara datas:order_date._count,_total,_avgpara métricas:orders_count,revenue_total,ticket_avg.
Isso reduz a necessidade de “abrir o cálculo” para entender o que é cada coluna.
Evite nomes genéricos e sobrecarregados
Nomes como value, status, type sem contexto são armadilhas. Prefira order_status, payment_status, customer_type. Em queries com múltiplas entidades, o contexto no nome evita confusão e erros de join/agrupamento.
Não renomeie por renomear
Se a coluna já tem um nome bom e padronizado no schema, manter o nome ajuda consistência. Renomeie quando:
- o nome original é ambíguo ou ruim;
- você está criando um campo derivado;
- você precisa padronizar nomes vindos de fontes diferentes no mesmo resultado.
Estrutura de query: organize como um pipeline de transformação
Separação por etapas: base, enriquecimento, cálculo, saída
Uma estrutura legível costuma seguir etapas claras. Mesmo sem repetir o capítulo de CTEs, vale usar a ideia de “pipeline”: cada etapa faz uma coisa e prepara a próxima. O objetivo é evitar uma única query monolítica com dezenas de expressões e regras misturadas.
Uma forma prática de pensar:
- Base: seleciona colunas essenciais e aplica filtros fundamentais (escopo).
- Enriquecimento: adiciona atributos de outras tabelas (dimensões).
- Cálculo: cria campos derivados e métricas.
- Saída: seleciona e ordena as colunas finais, com nomes prontos para consumo.
Evite lógica duplicada: centralize regras
Quando a mesma regra aparece em vários lugares (por exemplo, a definição de “pedido válido”), a query fica difícil de manter: alguém ajusta um trecho e esquece outro. Centralize a regra em um único ponto e reutilize o resultado. Mesmo dentro de uma única query, isso pode ser feito criando um campo derivado e usando-o depois, em vez de repetir a expressão.
SELECT o.order_id, o.order_date, o.status, CASE WHEN o.status IN ('paid', 'shipped', 'delivered') THEN 1 ELSE 0 END AS is_valid_orderFROM orders AS o;Depois, em vez de repetir status IN (...) em múltiplos filtros e cálculos, use is_valid_order (quando o banco permitir referenciar alias na mesma camada, ou em uma camada acima).
Ordem lógica das colunas no SELECT final
Uma ordem previsível melhora leitura e uso em relatórios. Um padrão comum:
- Chaves e identificadores:
customer_id,order_id. - Datas e períodos:
order_date,month. - Dimensões descritivas:
customer_segment,region. - Métricas:
orders_count,revenue_total. - Flags e campos técnicos:
is_valid_order,source_system.
Isso reduz o tempo para encontrar campos e evita que métricas fiquem “perdidas” no meio de dimensões.
Passo a passo prático: refatorando uma query para ficar legível
A seguir, um exemplo de refatoração focada em alias, formatação, nomes e estrutura. A ideia é pegar uma query funcional, porém difícil de ler, e transformá-la em algo fácil de revisar e manter.
Passo 1: identifique o objetivo e as entidades envolvidas
Antes de mexer no SQL, escreva em uma frase o que a query entrega. Exemplo: “Listar pedidos de 2025 com nome do cliente e valor total, marcando se o pedido é válido para receita”. Isso ajuda a separar o que é essencial do que é ruído.
Passo 2: comece com uma versão “crua” (pouco legível)
Exemplo de query que funciona, mas é difícil de manter:
select customer_id,customer_name,order_id,order_date,total_amount,case when status in('paid','shipped','delivered') then 1 else 0 end valid from customers c join orders o on o.customer_id=c.customer_id where order_date>='2025-01-01' and order_date<'2026-01-01' and (status<>'canceled' or status is null) order by order_date desc;Problemas típicos aqui: palavras-chave em minúsculas misturadas, alias inconsistentes (tabela customers sem AS, colunas sem prefixo), nome de coluna derivada genérico (valid), condições comprimidas em uma linha e uma regra confusa (status <> 'canceled' OR status IS NULL) que pode não refletir a intenção.
Passo 3: aplique formatação e padronize alias de tabela
Primeiro, deixe a estrutura visível e padronize alias:
SELECT c.customer_id, c.customer_name, o.order_id, o.order_date, o.total_amount, CASE WHEN o.status IN ('paid', 'shipped', 'delivered') THEN 1 ELSE 0 END AS validFROM customers AS cINNER JOIN orders AS o ON o.customer_id = c.customer_idWHERE o.order_date >= DATE '2025-01-01' AND o.order_date < DATE '2026-01-01' AND (o.status <> 'canceled' OR o.status IS NULL)ORDER BY o.order_date DESC;Mesmo sem mudar a lógica, a leitura melhora. Agora fica fácil discutir a regra do status.
Passo 4: melhore nomes de colunas derivadas e explicite intenção
Troque valid por um nome semântico. Se a regra é “pedido válido para receita”, use is_valid_for_revenue. Se é “pedido não cancelado”, use is_not_canceled. Nomeie conforme o uso.
SELECT c.customer_id, c.customer_name, o.order_id, o.order_date, o.total_amount, CASE WHEN o.status IN ('paid', 'shipped', 'delivered') THEN 1 ELSE 0 END AS is_valid_for_revenueFROM customers AS cINNER JOIN orders AS o ON o.customer_id = c.customer_idWHERE o.order_date >= DATE '2025-01-01' AND o.order_date < DATE '2026-01-01' AND (o.status <> 'canceled' OR o.status IS NULL)ORDER BY o.order_date DESC;Passo 5: reescreva condições para reduzir ambiguidade
Condições com OR costumam ser pontos de erro e de leitura lenta. Se a intenção é “excluir cancelados”, uma forma mais clara é: “status é nulo ou status é diferente de canceled”. Ainda é OR, mas você pode colocar primeiro o caso especial (nulo) e documentar. Se a intenção real é “considerar apenas status válidos para receita”, então o filtro deveria usar a mesma lista do CASE, evitando divergência entre filtro e flag.
Exemplo alinhando filtro e flag (uma única fonte de verdade):
SELECT c.customer_id, c.customer_name, o.order_id, o.order_date, o.total_amount, CASE WHEN o.status IN ('paid', 'shipped', 'delivered') THEN 1 ELSE 0 END AS is_valid_for_revenueFROM customers AS cINNER JOIN orders AS o ON o.customer_id = c.customer_idWHERE o.order_date >= DATE '2025-01-01' AND o.order_date < DATE '2026-01-01' AND o.status IN ('paid', 'shipped', 'delivered')ORDER BY o.order_date DESC;Agora a query fica mais previsível: o resultado contém apenas pedidos válidos para receita, e a flag vira redundante (você pode removê-la se não precisar). Se você precisa manter pedidos inválidos no resultado para auditoria, então não filtre por status; apenas crie a flag e deixe o consumidor decidir.
Passo 6: organize o SELECT final por grupos e evite redundâncias
Se a query é para consumo em relatório, organize colunas por grupos (IDs, datas, dimensões, métricas). Também avalie se campos redundantes devem sair. Exemplo mantendo a flag para auditoria:
SELECT o.order_id, o.customer_id, c.customer_name, o.order_date, o.status AS order_status, o.total_amount, CASE WHEN o.status IN ('paid', 'shipped', 'delivered') THEN 1 ELSE 0 END AS is_valid_for_revenueFROM orders AS oINNER JOIN customers AS c ON c.customer_id = o.customer_idWHERE o.order_date >= DATE '2025-01-01' AND o.order_date < DATE '2026-01-01'ORDER BY o.order_date DESC;Note que order_status deixa explícito o contexto, e a ordem das colunas começa por identificadores.
Checklist rápido de legibilidade para usar no dia a dia
Alias
- Alias de tabela são consistentes e não genéricos demais.
- Colunas sempre qualificadas com alias de tabela quando há mais de uma tabela.
- Alias de coluna descrevem significado de negócio.
Formatação
- Palavras-chave em maiúsculas (ou minúsculas), mas sempre no mesmo padrão.
- Uma condição por linha em WHERE/ON quando houver múltiplas regras.
- Sem
SELECT *em queries de relatório/produção.
Nomes
- Sem nomes genéricos sem contexto (
status,type,value). - Uso consistente de
snake_casee padrões comois_,_at,_count. - Evita alias enganosos que não correspondem à regra aplicada.
Estrutura
- Query organizada em blocos previsíveis e fáceis de escanear.
- Regras de negócio centralizadas para evitar duplicação e divergência.
- SELECT final com colunas em ordem lógica para consumo.