A etapa técnica de SQL em processos seletivos assusta muita gente — mas a realidade é que a maioria das avaliações testa um conjunto relativamente pequeno de conceitos. Quem domina esses conceitos e sabe explicar o próprio raciocínio passa na grande maioria dos processos para analista de dados.
Este guia reúne as 20 perguntas de SQL mais cobradas em entrevistas técnicas no Brasil, com respostas completas e exemplos de código. Use como checklist de preparação.
Como as entrevistas técnicas de SQL funcionam
Existem três formatos principais:
1. Teste assíncrono (HackerRank, StrataScratch, LeetCode SQL) Você recebe um problema e tem um tempo para resolver. O código é avaliado automaticamente. Foco em acertar a saída correta.
2. Teste enviado por e-mail Um conjunto de queries para escrever, geralmente em cima de um schema enviado junto. Você retorna um arquivo SQL ou planilha com as respostas.
3. Live coding com entrevistador O formato mais exigente. Você resolve o problema em tempo real, compartilhando a tela. O entrevistador pode fazer perguntas, pedir explicações e introduzir variações do problema.
Para os três formatos, o mesmo conjunto de habilidades é testado. A diferença é que no live coding, a capacidade de explicar o raciocínio é tão importante quanto a solução em si.
As 20 perguntas mais comuns
Usaremos um schema simplificado ao longo dos exemplos:
-- Tabelas de referência
clientes (id_cliente, nome, cidade, data_cadastro)
pedidos (id_pedido, id_cliente, data_pedido, valor_total, status)
produtos (id_produto, nome_produto, categoria, preco_unitario)
itens_pedido (id_pedido, id_produto, quantidade, preco_unitario)
vendedores (id_vendedor, nome, regiao, data_admissao)
pedidos_vendedor (id_pedido, id_vendedor)
1. Qual a diferença entre INNER JOIN, LEFT JOIN e RIGHT JOIN?
Resposta conceitual:
INNER JOIN— retorna apenas as linhas que têm correspondência em ambas as tabelasLEFT JOIN— retorna todas as linhas da tabela da esquerda, comNULLonde não há correspondência na direitaRIGHT JOIN— o inverso doLEFT JOIN(raramente usado na prática; geralmente reescrito comoLEFT JOIN)
Exemplo prático:
-- INNER JOIN: apenas clientes que têm pedidos
SELECT c.nome, COUNT(p.id_pedido) AS total_pedidos
FROM clientes c
INNER JOIN pedidos p ON c.id_cliente = p.id_cliente
GROUP BY c.nome;
-- LEFT JOIN: todos os clientes, incluindo os sem pedidos (retornam NULL)
SELECT c.nome, COUNT(p.id_pedido) AS total_pedidos
FROM clientes c
LEFT JOIN pedidos p ON c.id_cliente = p.id_cliente
GROUP BY c.nome;
Como responder na entrevista: explique o conceito, depois mostre que sabe quando usar cada um. Casos onde LEFT JOIN é necessário (clientes sem pedidos, produtos sem vendas) são mais comuns na prática do que INNER JOIN puro.
2. Qual a diferença entre WHERE e HAVING?
Resposta:
WHEREfiltra linhas antes da agregaçãoHAVINGfiltra grupos depois da agregação
-- Clientes que fizeram mais de 5 pedidos com valor total acima de R$ 1.000
SELECT id_cliente, COUNT(*) AS qtd_pedidos, SUM(valor_total) AS total_gasto
FROM pedidos
WHERE status = 'concluido' -- WHERE: filtra linhas antes de agrupar
GROUP BY id_cliente
HAVING COUNT(*) > 5 -- HAVING: filtra grupos depois de agrupar
AND SUM(valor_total) > 1000;
Erro comum que o entrevistador vai testar: tentar usar uma função de agregação no WHERE — isso gera erro. Se a condição usa COUNT, SUM, AVG, etc., ela vai no HAVING.
Veja mais sobre esse tópico em nosso artigo sobre quando usar WHERE vs HAVING.
3. Como encontrar duplicatas em uma tabela?
-- Encontrar e-mails duplicados na tabela de clientes
SELECT email, COUNT(*) AS ocorrencias
FROM clientes
GROUP BY email
HAVING COUNT(*) > 1
ORDER BY ocorrencias DESC;
Variação comum: “Como deletar as duplicatas mantendo apenas um registro?” — isso envolve CTEs com ROW_NUMBER(), que aparece mais em vagas plenas.
4. Como funciona o GROUP BY? Quais colunas podem aparecer no SELECT?
Resposta:
GROUP BY agrupa linhas com o mesmo valor nas colunas especificadas. No SELECT, só podem aparecer:
- Colunas que estão no
GROUP BY - Funções de agregação (
COUNT,SUM,AVG,MIN,MAX)
-- Receita total por categoria de produto em 2025
SELECT
p.categoria,
SUM(ip.quantidade * ip.preco_unitario) AS receita_total,
COUNT(DISTINCT ip.id_pedido) AS num_pedidos
FROM itens_pedido ip
JOIN produtos p ON ip.id_produto = p.id_produto
JOIN pedidos ped ON ip.id_pedido = ped.id_pedido
WHERE YEAR(ped.data_pedido) = 2025
GROUP BY p.categoria
ORDER BY receita_total DESC;
5. O que é uma subquery? Quando usar subquery vs JOIN?
Subquery é uma query dentro de outra query, usada no WHERE, FROM ou SELECT.
-- Clientes que gastaram acima da média geral
SELECT nome, total_gasto
FROM (
SELECT c.nome, SUM(p.valor_total) AS total_gasto
FROM clientes c
JOIN pedidos p ON c.id_cliente = p.id_cliente
GROUP BY c.nome
) resumo_clientes
WHERE total_gasto > (SELECT AVG(valor_total) FROM pedidos);
Quando usar subquery vs JOIN:
- Use
JOINquando precisa de colunas de múltiplas tabelas - Use subquery quando o resultado da subconsulta é um valor escalar ou uma lista de referência
- Em termos de performance,
JOINgeralmente é mais eficiente para grandes volumes
6. O que é uma CTE (WITH) e por que usá-la?
CTE (Common Table Expression) é uma query nomeada temporária definida antes da query principal com WITH. Não existe no banco — é calculada na hora da execução.
-- Receita por cliente e ranking dentro da cidade
WITH receita_por_cliente AS (
SELECT
c.id_cliente,
c.nome,
c.cidade,
SUM(p.valor_total) AS receita_total
FROM clientes c
JOIN pedidos p ON c.id_cliente = p.id_cliente
WHERE p.status = 'concluido'
GROUP BY c.id_cliente, c.nome, c.cidade
)
SELECT
nome,
cidade,
receita_total,
RANK() OVER (PARTITION BY cidade ORDER BY receita_total DESC) AS ranking_cidade
FROM receita_por_cliente;
Por que usar CTEs:
- Legibilidade — quebra queries complexas em partes nomeadas
- Reutilização — pode referenciar a mesma CTE múltiplas vezes
- Recursividade — CTEs recursivas para hierarquias (avançado)
7. Quais funções de janela você conhece? Como ROW_NUMBER difere de RANK?
Funções de janela calculam valores em relação a um “janela” de linhas sem colapsar os resultados em grupos.
SELECT
nome,
regiao,
receita_total,
ROW_NUMBER() OVER (PARTITION BY regiao ORDER BY receita_total DESC) AS row_num,
RANK() OVER (PARTITION BY regiao ORDER BY receita_total DESC) AS rank_posicao,
DENSE_RANK() OVER (PARTITION BY regiao ORDER BY receita_total DESC) AS dense_rank
FROM vendas_por_vendedor;
Diferença:
ROW_NUMBER— número sequencial único, sem empates (dois valores iguais recebem números diferentes)RANK— empates recebem o mesmo número, e o próximo pula (1, 2, 2, 4)DENSE_RANK— empates recebem o mesmo número, sem pular (1, 2, 2, 3)
8. Como calcular a diferença entre o valor atual e o anterior (variação período a período)?
-- Receita mensal e variação percentual em relação ao mês anterior
WITH receita_mensal AS (
SELECT
DATE_TRUNC('month', data_pedido) AS mes,
SUM(valor_total) AS receita
FROM pedidos
WHERE status = 'concluido'
GROUP BY DATE_TRUNC('month', data_pedido)
)
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 variacao_pct
FROM receita_mensal
ORDER BY mes;
LAG() acessa o valor da linha anterior. LEAD() acessa o valor da linha seguinte. São clássicos em análises de tendência.
9. Como encontrar o segundo maior valor em uma coluna?
Essa pergunta aparece em muitos processos como teste de conhecimento de funções de janela ou subqueries.
-- Abordagem 1: com subquery (mais comum em banco de dados que não suportam LIMIT + OFFSET)
SELECT MAX(valor_total) AS segundo_maior
FROM pedidos
WHERE valor_total < (SELECT MAX(valor_total) FROM pedidos);
-- Abordagem 2: com DENSE_RANK (mais robusta e recomendada)
SELECT valor_total
FROM (
SELECT valor_total,
DENSE_RANK() OVER (ORDER BY valor_total DESC) AS dr
FROM pedidos
) ranked
WHERE dr = 2
LIMIT 1;
Dica de entrevista: sempre pergunte “como tratar empates?” O entrevistador quer ver que você pensa nos edge cases.
10. Como funciona o CASE WHEN?
CASE WHEN é o equivalente de um if/else no SQL. Cria colunas calculadas com lógica condicional.
-- Classificar clientes por volume de compras
SELECT
c.nome,
SUM(p.valor_total) AS total_gasto,
CASE
WHEN SUM(p.valor_total) >= 10000 THEN 'VIP'
WHEN SUM(p.valor_total) >= 5000 THEN 'Premium'
WHEN SUM(p.valor_total) >= 1000 THEN 'Regular'
ELSE 'Novo'
END AS categoria_cliente
FROM clientes c
LEFT JOIN pedidos p ON c.id_cliente = p.id_cliente
GROUP BY c.nome
ORDER BY total_gasto DESC;
11. Qual a diferença entre COUNT(*), COUNT(coluna) e COUNT(DISTINCT coluna)?
SELECT
COUNT(*) AS total_linhas, -- conta todas as linhas
COUNT(id_cliente) AS clientes_com_pedido, -- exclui NULLs
COUNT(DISTINCT id_cliente) AS clientes_unicos -- conta valores únicos
FROM pedidos;
Erro comum: usar COUNT(*) quando a pergunta é “quantos clientes distintos”. O entrevistador vai testar isso.
12. Como fazer um self join? Quando isso é necessário?
Self join é quando uma tabela faz JOIN com ela mesma. Útil para comparar linhas dentro da mesma tabela ou navegar hierarquias.
-- Listar empregados com seus respectivos gerentes
SELECT
e.nome AS funcionario,
g.nome AS gerente
FROM funcionarios e
LEFT JOIN funcionarios g ON e.id_gerente = g.id_funcionario
ORDER BY gerente, funcionario;
13. Como calcular uma média móvel?
-- Média móvel de 3 dias de receita
SELECT
data_pedido,
SUM(valor_total) AS receita_dia,
AVG(SUM(valor_total)) OVER (
ORDER BY data_pedido
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS media_movel_3d
FROM pedidos
WHERE status = 'concluido'
GROUP BY data_pedido
ORDER BY data_pedido;
A cláusula ROWS BETWEEN 2 PRECEDING AND CURRENT ROW define a janela como “as 2 linhas anteriores mais a atual”.
14. O que é um índice e como ele afeta a performance?
Resposta conceitual para entrevistas: Um índice é uma estrutura de dados auxiliar que o banco mantém para acelerar consultas em colunas específicas. Funciona como o índice de um livro: em vez de ler tudo, o banco vai diretamente à posição certa.
Quando usar:
- Colunas usadas frequentemente em
WHERE,JOINeORDER BY - Colunas de foreign key
Quando não usar:
- Tabelas pequenas (o overhead não compensa)
- Colunas que sofrem muitas atualizações (índice precisa ser reconstruído a cada escrita)
15. Como fazer UNION vs UNION ALL?
-- UNION: remove duplicatas (mais lento)
SELECT cidade FROM clientes
UNION
SELECT cidade FROM fornecedores;
-- UNION ALL: mantém duplicatas (mais rápido)
SELECT cidade FROM clientes
UNION ALL
SELECT cidade FROM fornecedores;
Regra: as queries combinadas precisam ter o mesmo número de colunas e tipos compatíveis. Prefira UNION ALL quando souber que não há duplicatas ou quando elas são desejadas.
16. Como deletar registros duplicados mantendo apenas o mais recente?
-- Manter apenas o registro mais recente para cada e-mail
DELETE FROM clientes
WHERE id_cliente NOT IN (
SELECT MAX(id_cliente)
FROM clientes
GROUP BY email
);
Variação com CTE (mais legível):
WITH duplicatas AS (
SELECT id_cliente,
ROW_NUMBER() OVER (PARTITION BY email ORDER BY data_cadastro DESC) AS rn
FROM clientes
)
DELETE FROM clientes
WHERE id_cliente IN (SELECT id_cliente FROM duplicatas WHERE rn > 1);
17. O que é NULL e como tratar corretamente?
Pontos importantes:
NULLnão é zero, não é string vazia — é ausência de valorNULL = NULLretornaNULL(nãoTRUE) — useIS NULLouIS NOT NULL- Funções como
COALESCEeNULLIFajudam a tratar NULLs
-- COALESCE: retorna o primeiro valor não nulo
SELECT nome, COALESCE(telefone, 'Não informado') AS telefone_formatado
FROM clientes;
-- NULLIF: retorna NULL se dois valores são iguais (evita divisão por zero)
SELECT receita / NULLIF(custo, 0) AS margem
FROM produtos;
18. Como calcular percentual de um total?
-- Participação de cada categoria na receita total
SELECT
p.categoria,
SUM(ip.quantidade * ip.preco_unitario) AS receita,
ROUND(
SUM(ip.quantidade * ip.preco_unitario) * 100.0 /
SUM(SUM(ip.quantidade * ip.preco_unitario)) OVER (),
2
) AS pct_receita
FROM itens_pedido ip
JOIN produtos p ON ip.id_produto = p.id_produto
GROUP BY p.categoria
ORDER BY receita DESC;
O SUM(...) OVER () sem partição calcula o total geral — a janela abrange todas as linhas.
19. Qual a diferença entre TRUNCATE e DELETE?
DELETE | TRUNCATE | |
|---|---|---|
| Remove linhas específicas | Sim (com WHERE) | Não — remove todas |
| Pode ser revertido | Sim (se dentro de transação) | Depende do banco |
| Velocidade | Mais lento (log por linha) | Muito mais rápido |
| Reseta auto-increment | Não | Sim (na maioria dos bancos) |
| Dispara triggers | Sim | Não |
20. Como otimizar uma query lenta?
Essa pergunta aparece em vagas plenas e sênior. Uma resposta estruturada:
1. Identifique o gargalo com EXPLAIN / EXPLAIN ANALYZE
EXPLAIN ANALYZE
SELECT c.nome, COUNT(p.id_pedido)
FROM clientes c
JOIN pedidos p ON c.id_cliente = p.id_cliente
GROUP BY c.nome;
2. Verifique a presença de índices nas colunas usadas em JOIN, WHERE e ORDER BY
3. Evite SELECT * — selecione apenas as colunas necessárias
4. Substitua subqueries correlacionadas por JOINs quando possível
5. Use EXISTS em vez de IN para listas grandes
6. Limite o volume de dados antes de fazer JOINs — filtre o máximo possível nas subqueries ou CTEs antes de combinar tabelas grandes
Como se preparar para a etapa técnica de SQL
O maior erro na preparação é estudar muita teoria e praticar pouco. Avaliações técnicas medem o que você consegue fazer, não o que você sabe explicar.
Uma rotina de preparação eficaz:
- Estude o conceito — entenda o que o comando faz e quando usá-lo
- Escreva a query — sem copiar, do zero, em cima de um dataset real
- Explique o raciocínio em voz alta — simule o live coding
- Varie o problema — o que muda se a pergunta for ligeiramente diferente?
Se você ainda está no começo, o artigo SQL para iniciantes: o guia completo é o ponto de partida antes de mergulhar nas perguntas de entrevista.
Pronto para ir além dos tutoriais?
Entrevistas técnicas cobram prática com dados reais em situações de pressão. A melhor preparação combina teoria bem fundamentada com exercícios progressivos que simulam os problemas que aparecem no trabalho.
O Curso SQL do Zero ao Avançado da Blast inclui módulos específicos de preparação para processos seletivos — com exercícios baseados nos tipos de perguntas que aparecem neste guia, em ambientes que simulam avaliações técnicas reais. É a preparação mais direta que existe para passar de forma consistente nas etapas técnicas de SQL.
Perguntas frequentes
Preciso saber todas as 20 perguntas para passar em uma vaga júnior?
Não. Para vagas júnior, as questões 1 a 9 cobrem a maioria dos processos. As questões 10 a 20 aparecem mais em vagas plenas e sênior, ou em empresas com bar técnico mais alto.
Qual banco de dados devo usar para praticar?
PostgreSQL é a melhor opção — é gratuito, open source, amplamente usado no mercado brasileiro e tem uma das implementações mais completas de SQL padrão. Instale localmente ou use o SQLFiddle para praticar online.
Existe alguma plataforma online para praticar perguntas de entrevista SQL?
Sim. StrataScratch, LeetCode (seção SQL), HackerRank e Mode Analytics têm coleções de problemas reais de entrevistas, classificados por dificuldade e empresa.
O entrevistador espera que eu acerte na primeira tentativa?
Não necessariamente — especialmente no live coding. O que o entrevistador avalia é a forma como você pensa no problema, como reage a erros e se consegue iterar até chegar na solução. Verbalizar o raciocínio é tão importante quanto o código.
