Além da ementa · tema 19 de 30
SQL para engenharia de dados
40 min · 2 vídeos · 13 cards · 1 drill
Por que cai
SQL é o único tema em que a banca pode pedir para você escrever ou descrever a consulta na hora. É teste de fluência: quem hesita entre ROW_NUMBER e GROUP BY entrega que usa SQL só para conferir resultado, não para transformar dado.
Pré-teste · 1 de 2
responda antes de verVocê quer as contas que NÃO tiveram transação em janeiro. A tabela de transações permite id_conta nulo. Qual construção é mais segura?
Vídeo · português · 31 min
Em uma frase
O SQL que a sabatina cobra é o de transformação: anti-join, CTE, window function, leitura de plano — e saber que o SQL analítico sobre arquivos joga com regras diferentes do SQL transacional sobre índice.
Join, anti-join e o perigo do NOT IN
Anti-join é "traga o que não tem correspondência". Duas formas seguras e uma armadilha:
-- Forma canônica: NOT EXISTS
SELECT c.id_conta
FROM contas c
WHERE NOT EXISTS (
SELECT 1 FROM transacoes t WHERE t.id_conta = c.id_conta
);
-- Equivalente: LEFT JOIN com IS NULL
SELECT c.id_conta
FROM contas c
LEFT JOIN transacoes t ON t.id_conta = c.id_conta
WHERE t.id_transacao IS NULL;
Agregação e HAVING
WHERE filtra linha antes da agregação. HAVING filtra grupo depois dela. "Clientes com mais de 50 transações no mês" precisa de HAVING, porque a contagem só existe depois do GROUP BY.
CTE no lugar de subquery aninhada
CTE nomeia o passo. Três subqueries aninhadas obrigam a leitura de dentro para fora; a mesma lógica em CTE se lê na ordem em que foi pensada, e cada bloco pode ser executado isolado durante a depuração. Em consulta de pipeline, isso vale mais do que qualquer ganho de performance — o otimizador costuma produzir o mesmo plano.
Window functions: o coração do tema
window function— Função que calcula um valor por linha olhando um conjunto de linhas relacionadas (a janela), sem colapsar o resultado como o GROUP BY faz.-- Deduplicar / última transação por conta
WITH ordenadas AS (
SELECT t.*,
ROW_NUMBER() OVER (
PARTITION BY id_conta
ORDER BY data_transacao DESC, id_transacao DESC
) AS rn
FROM transacoes t
)
SELECT * FROM ordenadas WHERE rn = 1;
-- Variação em relação à transação anterior
SELECT id_conta, data_transacao, valor,
valor - LAG(valor) OVER (PARTITION BY id_conta ORDER BY data_transacao) AS variacao
FROM transacoes;
-- Gasto acumulado no mês
SELECT id_conta, data_transacao, valor,
SUM(valor) OVER (
PARTITION BY id_conta ORDER BY data_transacao
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS acumulado
FROM transacoes;
| Função | Em caso de empate | Uso típico |
|---|---|---|
| ROW_NUMBER | Desempata arbitrariamente, nunca repete | Deduplicar, pegar a linha mais recente |
| RANK | Repete o número e pula as posições seguintes | Ranking em que o buraco é informativo |
| DENSE_RANK | Repete o número e não pula | Top N por categoria mantendo empates |
-- Mesma dedupe, com QUALIFY
SELECT *
FROM transacoes
QUALIFY ROW_NUMBER() OVER (PARTITION BY id_transacao ORDER BY data_ingestao DESC) = 1;
Ler o plano de execução
EXPLAIN mostra o que o motor pretende fazer. O que procurar, em ordem:
- 1Varredura completa onde deveria haver poda de partição — quase sempre é filtro com função aplicada sobre a coluna particionada.
- 2Estratégia de join escolhida: broadcast, sort-merge ou hash, e se ela faz sentido para o tamanho dos lados.
- 3Estimativa de linhas muito distante do real, sinal de estatística desatualizada.
- 4Ordenação ou agregação que você não pediu, geralmente induzida por DISTINCT ou por window mal escrita.
- 5Ordem dos joins: filtrar cedo e juntar tarde costuma valer mais que qualquer hint.
Índice: quando ajuda e quando atrapalha
Ajuda em consulta seletiva por chave — cadastro por CPF, transação por id. Atrapalha em tabela com escrita intensa, porque cada INSERT mantém a estrutura, e quando a consulta lê boa parte da tabela, caso em que a varredura sequencial sai mais barata que o acesso aleatório.
SQL analítico não é SQL transacional
| Aspecto | OLTP (Postgres, core bancário) | Analítico (Spark SQL, Athena) |
|---|---|---|
| Unidade de acesso | Linha, via índice | Arquivo e coluna, via varredura podada |
| Otimização principal | Índice e estatística de coluna | Particionamento, ordenação, poda por estatística do Parquet |
| Custo dominante | Busca aleatória em disco | Leitura de dados e shuffle na rede |
| Atualização | UPDATE e DELETE por linha, transacional | Reescrita de arquivo; MERGE só com formato de tabela como Delta |
| Transação | ACID multi-tabela | ACID por tabela, quando há Delta ou Iceberg |
Dizer isso em voz alta muda o tom da resposta: você não está usando "o mesmo SQL num lugar maior", está usando uma linguagem parecida sobre um motor com outra física de custo.
Se quiser outro ângulo
Como cai na sabatina
“Como você pegaria a última transação de cada conta?”
Erros comuns
- Usar NOT IN com subconsulta que pode retornar NULL. Um único NULL faz a condição virar desconhecida e o resultado volta vazio. NOT EXISTS não tem esse problema.
- Achar que GROUP BY resolve 'a última transação de cada conta'. GROUP BY colapsa a linha e você perde as outras colunas; o certo é ROW_NUMBER particionado pela conta.
- Filtrar window function no WHERE. A janela é avaliada depois do WHERE; ou você envolve numa CTE e filtra fora, ou usa QUALIFY onde o motor suporta.
- Confundir RANK com DENSE_RANK e com ROW_NUMBER. Em empate, ROW_NUMBER desempata arbitrariamente, RANK pula posições e DENSE_RANK não pula.
- Pedir índice para tabela analítica em Parquet. Em Spark SQL e Athena não existe índice como no OLTP; o que ganha é particionamento, ordenação de arquivos e poda por estatística.
- Empilhar subquery aninhada onde uma CTE resolveria. Além de ilegível, dificulta reaproveitar o mesmo bloco e conversar sobre o plano com outra pessoa.
Flashcards
Card 1 de 13
0 certos · 0 a reverDrill
Qual construção SQL você usaria?
Esquema fictício: transacoes(id_transacao, id_conta, data_transacao, valor, estabelecimento, canal), contas(id_conta, id_cliente, tipo_conta, data_abertura), clientes(id_cliente, nome, cpf, segmento). Para cada requisito, diga em voz alta qual construção resolve, antes de conferir.
1.A última transação de cada conta, com valor e estabelecimento.
2.Contas que nunca transacionaram.
3.Clientes com mais de 50 transações no mês.
4.Variação de valor entre cada transação e a anterior da mesma conta.
5.Saldo acumulado de gastos por conta ao longo do mês.
6.As três maiores transações de cada segmento de cliente, mantendo empates na terceira posição.
7.Remover transações duplicadas pela mesma chave de autorização, mantendo a de ingestão mais recente, num motor que suporta a cláusula.
8.Total transacionado por conta, incluindo contas sem nenhuma transação, com zero no lugar de nulo.
Quiz · 1 de 2
Em qual situação DENSE_RANK é a escolha certa em vez de ROW_NUMBER?
Explique para um gerente
Explique para um analista de negócio, sem escrever código, o que uma window function faz de diferente de um GROUP BY.
Responda em voz alta antes de seguir. Se travar numa palavra técnica, é sinal de que ainda não entendeu essa parte.
Perguntas de sabatina deste tema
- · Como você pegaria a última transação de cada conta? Descreva a consulta e diga por que não usa GROUP BY.
- · Uma consulta que roda em 2 segundos no ambiente de desenvolvimento leva 8 minutos em produção sobre a tabela real de transações. Como você investiga?
Para ir além