Análise de Vendas com SQL: do básico ao avançado
/ 3 min read
Table of Contents
Contexto
Imagine que você recebeu acesso ao banco de dados de uma loja de e-commerce com três tabelas principais:
-- Clientescustomers (id, name, city, created_at)
-- Pedidosorders (id, customer_id, total, status, created_at)
-- Itens do pedidoorder_items (id, order_id, product, category, quantity, price)Abaixo estão as análises que um Data Analyst faria nas primeiras horas de acesso a esses dados.
1. Visão geral do negócio
Antes de qualquer análise, entender o tamanho dos dados:
SELECT COUNT(DISTINCT customer_id) AS total_clientes, COUNT(*) AS total_pedidos, SUM(total) AS receita_total, AVG(total) AS ticket_medio, MIN(created_at) AS primeiro_pedido, MAX(created_at) AS ultimo_pedidoFROM ordersWHERE status = 'completed';2. Receita por mês (crescimento MoM)
Acompanhar a evolução da receita mês a mês usando LAG para calcular a variação:
WITH receita_mensal AS ( SELECT DATE_TRUNC('month', created_at) AS mes, SUM(total) AS receita FROM orders WHERE status = 'completed' GROUP BY 1)SELECT mes, receita, LAG(receita) OVER (ORDER BY mes) AS receita_mes_anterior, ROUND( (receita - LAG(receita) OVER (ORDER BY mes)) / LAG(receita) OVER (ORDER BY mes) * 100, 2 ) AS crescimento_pctFROM receita_mensalORDER BY mes;Por que isso importa: crescimento MoM é uma das métricas mais pedidas em reuniões de negócio. Saber escrever essa query do zero é diferencial em entrevistas.
3. Top 10 clientes por receita
Ranking com RANK() para identificar os melhores clientes:
SELECT c.name, c.city, COUNT(o.id) AS total_pedidos, SUM(o.total) AS receita_total, AVG(o.total) AS ticket_medio, RANK() OVER (ORDER BY SUM(o.total) DESC) AS rankingFROM customers cJOIN orders o ON c.id = o.customer_idWHERE o.status = 'completed'GROUP BY c.id, c.name, c.cityORDER BY receita_total DESCLIMIT 10;4. Categoria de produto mais rentável
Quais categorias geram mais receita e têm maior volume?
SELECT oi.category, COUNT(DISTINCT o.id) AS total_pedidos, SUM(oi.quantity) AS unidades_vendidas, SUM(oi.price * oi.quantity) AS receita_total, ROUND( SUM(oi.price * oi.quantity) / SUM(SUM(oi.price * oi.quantity)) OVER () * 100, 1 ) AS participacao_pctFROM order_items oiJOIN orders o ON oi.order_id = o.idWHERE o.status = 'completed'GROUP BY oi.categoryORDER BY receita_total DESC;5. Clientes em risco de churn
Identificar clientes que compraram antes mas sumiram nos últimos 90 dias — candidatos a uma campanha de reativação:
WITH ultima_compra AS ( SELECT customer_id, MAX(created_at) AS ultima_compra, COUNT(*) AS total_pedidos, SUM(total) AS receita_total FROM orders WHERE status = 'completed' GROUP BY customer_id)SELECT c.name, c.city, uc.ultima_compra, uc.total_pedidos, uc.receita_total, DATE_PART('day', NOW() - uc.ultima_compra) AS dias_sem_comprarFROM ultima_compra ucJOIN customers c ON uc.customer_id = c.idWHERE uc.ultima_compra < NOW() - INTERVAL '90 days' AND uc.receita_total > 500 -- apenas clientes relevantesORDER BY uc.receita_total DESC;Por que isso importa: churn silencioso é um dos maiores problemas de e-commerce. Detectar esses clientes antes que sumam definitivamente é ação de alto impacto com baixo custo.
O que essas queries mostram
| Conceito | Queries acima |
|---|---|
Agregações (SUM, AVG, COUNT) | #1, #3, #4 |
Window functions (RANK, LAG) | #2, #3 |
CTEs (WITH) | #2, #5 |
| JOINs entre tabelas | #3, #4, #5 |
| Filtros com datas | #2, #5 |
| Cálculo de participação (%) | #4 |
Dominar esses padrões cobre a grande maioria das análises do dia a dia de um Data Analyst — e são os tópicos mais cobrados em processos seletivos.
Próximos passos
Se quiser praticar com dados reais, alguns datasets públicos excelentes para SQL:
- Northwind — banco clássico de e-commerce, perfeito para queries de negócio
- Kaggle: Brazilian E-Commerce (Olist) — dados reais de uma empresa brasileira, ótimo para portfolio
- Mode Analytics SQL Tutorial — exercícios práticos com datasets reais