---
title: "teste de SQL para júnior: exercícios com respostas e como explicar"
url: "https://eu.dev.br/carreira/teste-sql-junior/"
markdown_url: "https://eu.dev.br/carreira/teste-sql-junior.MD"
description: "Um simulado de SQL com dados fictícios para praticar consultas, conferir resultados e explicar seu raciocínio sem decorar resposta."
date: "2026-10-12"
author: ""
---

# teste de SQL para júnior: exercícios com respostas e como explicar

Um simulado de SQL com dados fictícios para praticar consultas, conferir resultados e explicar seu raciocínio sem decorar resposta.


**Para se preparar para um teste de SQL de júnior, pratique filtros, agregações, junções e valores nulos — mas confira também se o resultado responde à pergunta certa.** Eu começaria com uma base pequena, um resultado calculado à mão e uma explicação em voz alta. Uma consulta que roda não prova que o número está correto: um `JOIN` pode duplicar valores e um filtro pode apagar clientes que deveriam aparecer.

Este é um **simulado autoral**, não uma prova vazada nem uma lista do que toda empresa cobra. Você pode usar os exercícios para vagas de [analista de dados júnior](/carreira/analista-de-dados-junior-brasil/), [estágio em dados](/carreira/estagio-analise-de-dados/) ou para revisar a base de [engenharia de dados](/carreira/engenheiro-de-dados-junior-brasil/). O escopo real depende do anúncio e das instruções da seleção.

## o que revisar antes do simulado

| habilidade | o que você precisa demonstrar |
|---|---|
| `SELECT`, `WHERE`, `ORDER BY` | escolher colunas, filtrar e ordenar sem mudar a regra do indicador |
| `GROUP BY`, `SUM`, `COUNT` | explicar o que cada linha agregada representa |
| `INNER JOIN`, `LEFT JOIN` | saber quais registros entram e quais ficam de fora |
| `NULL` | distinguir ausência de valor de zero ou texto vazio |
| `CASE` e `COALESCE` | expressar uma regra e tratar ausência sem esconder um problema |
| validação | comparar a consulta com uma conta simples e testar um caso limite |

CTEs e funções de janela podem aparecer, mas não substituem essa base. Se o anúncio pede BigQuery, PostgreSQL ou SQL Server, confira o dialeto. Sintaxe de datas, limites de linhas e algumas funções mudam entre bancos.

## prepare uma base pequena e reproduzível

Os exemplos abaixo usam **SQLite**. Rode em um banco local descartável ou em um ambiente de prática de sua escolha que aceite SQLite. Não execute `CREATE TABLE` em um banco de trabalho e não envie dados reais de clientes para um editor público.

Copie este bloco inteiro para um banco vazio. Os nomes e valores são fictícios. Os valores estão em **centavos**, para evitar depender de arredondamento de ponto flutuante neste treino.

```sql
CREATE TABLE clientes (
  id INTEGER PRIMARY KEY,
  nome TEXT NOT NULL,
  cidade TEXT
);

CREATE TABLE pedidos (
  id INTEGER PRIMARY KEY,
  cliente_id INTEGER NOT NULL REFERENCES clientes(id),
  data_pedido TEXT NOT NULL,
  status TEXT NOT NULL,
  valor_centavos INTEGER NOT NULL
);

INSERT INTO clientes (id, nome, cidade) VALUES
  (1, 'ana', 'recife'),
  (2, 'bruno', 'salvador'),
  (3, 'carla', NULL),
  (4, 'diego', 'recife');

INSERT INTO pedidos
  (id, cliente_id, data_pedido, status, valor_centavos) VALUES
  (101, 1, '2026-09-01', 'pago', 10000),
  (102, 1, '2026-09-03', 'cancelado', 5000),
  (103, 2, '2026-09-05', 'pago', 20000),
  (104, 2, '2026-10-01', 'pago', 30000),
  (105, 3, '2026-09-10', 'pago', 15000),
  (106, 1, '2026-09-30', 'pago', 8000);
```

As datas são texto em formato `YYYY-MM-DD`, sempre com mês e dia de dois dígitos. Nesse formato uniforme, a comparação textual usada no exercício funciona. Em um banco com coluna de data ou timestamp, use os tipos e as regras de fuso daquele ambiente.

Antes de resolver: são quatro clientes e seis pedidos. Diego não tem pedido; Carla não tem cidade cadastrada. Ana tem um cancelamento que não deve entrar na soma de pedidos pagos.

## exercício 1: filtre o período sem incluir o mês seguinte

**Pergunta:** quais pedidos pagos foram feitos em setembro de 2026? Mostre identificador, cliente e valor, em ordem de data e identificador.

Tente resolver antes de ler:

```sql
SELECT id, cliente_id, valor_centavos
FROM pedidos
WHERE status = 'pago'
  AND data_pedido >= '2026-09-01'
  AND data_pedido < '2026-10-01'
ORDER BY data_pedido, id;
```

| id | cliente_id | valor_centavos |
|---|---|---|
| 101 | 1 | 10000 |
| 103 | 2 | 20000 |
| 105 | 3 | 15000 |
| 106 | 1 | 8000 |

Minha explicação seria: “vou incluir o início de setembro e excluir o início de outubro. O pedido 104 fica fora do período, e o 102 fica fora pelo status”.

Esse intervalo também é um hábito útil quando você trabalha com timestamps: evita tentar representar o último instante de um mês. Ainda assim, confirme em qual fuso o período deve ser interpretado.

## exercício 2: calcule o total, não só uma soma que parece certa

**Pergunta:** qual é a soma dos valores dos pedidos pagos de setembro? Quantos pedidos entram nessa soma?

```sql
SELECT
  COUNT(*) AS quantidade_pedidos,
  SUM(valor_centavos) AS total_centavos
FROM pedidos
WHERE status = 'pago'
  AND data_pedido >= '2026-09-01'
  AND data_pedido < '2026-10-01';
```

Resultado: **4 pedidos e 53000 centavos, equivalentes a R$ 530,00**.

Confira à mão: `10000 + 20000 + 15000 + 8000 = 53000`.

Não chame esse número automaticamente de receita contábil. O exercício define soma de pedidos pagos; uma empresa pode ter regras adicionais para estorno, imposto, desconto ou reconhecimento de receita. Na entrevista, perguntar “o cancelamento deve ser excluído?” é melhor que decidir escondido.

Outro caso limite: se nenhum pedido passar pelo filtro, `COUNT(*)` retorna zero e `SUM` retorna `NULL`. Se o relatório exige zero, você pode usar `COALESCE(SUM(valor_centavos), 0)`. Explique que essa é uma decisão de apresentação, não a descoberta de um pagamento.

## exercício 3: mostre inclusive quem não comprou

**Pergunta:** para cada cliente, mostre quantidade de pedidos pagos e total pago em setembro. Inclua quem não teve pedido no período, com zero.

```sql
SELECT
  c.id,
  c.nome,
  COUNT(p.id) AS quantidade_pedidos,
  COALESCE(SUM(p.valor_centavos), 0) AS total_centavos
FROM clientes AS c
LEFT JOIN pedidos AS p
  ON p.cliente_id = c.id
  AND p.status = 'pago'
  AND p.data_pedido >= '2026-09-01'
  AND p.data_pedido < '2026-10-01'
GROUP BY c.id, c.nome
ORDER BY c.id;
```

| id | nome | quantidade_pedidos | total_centavos |
|---|---|---|---|
| 1 | ana | 2 | 18000 |
| 2 | bruno | 1 | 20000 |
| 3 | carla | 1 | 15000 |
| 4 | diego | 0 | 0 |

Aqui existem duas decisões importantes:

- **os filtros dos pedidos ficam no `ON`.** Assim, o cliente permanece no resultado mesmo sem um pedido elegível. Colocar `p.status = 'pago'` no `WHERE` eliminaria a linha sem pedido;
- **a contagem usa `COUNT(p.id)`, não `COUNT(*)`.** O `LEFT JOIN` preserva uma linha para Diego, mas o identificador do pedido é nulo. `COUNT(*)` contaria essa linha como se fosse um pedido.

Agrupar pelo identificador e pelo nome evita juntar duas pessoas diferentes só porque têm o mesmo nome. O nível de detalhe do resultado é **uma linha por cliente**.

## exercício 4: encontre clientes sem pedido pago no período

**Pergunta:** quem não teve nenhum pedido pago em setembro?

```sql
SELECT c.id, c.nome
FROM clientes AS c
WHERE NOT EXISTS (
  SELECT 1
  FROM pedidos AS p
  WHERE p.cliente_id = c.id
    AND p.status = 'pago'
    AND p.data_pedido >= '2026-09-01'
    AND p.data_pedido < '2026-10-01'
)
ORDER BY c.id;
```

Resultado: **4, diego**.

A consulta pergunta se existe algum pedido elegível para aquele cliente; `NOT EXISTS` mantém quem não tem nenhum. Outra solução seria usar o `LEFT JOIN` do exercício anterior e verificar `p.id IS NULL` no `WHERE`.

Não confunda “não teve pedido pago em setembro” com “nunca comprou”. Um cliente com pagamento só em outubro também deveria aparecer na resposta de setembro. Para testar isso, acrescente esse cenário em uma cópia da base e confira as duas soluções.

## exercício 5: trate cidade ausente sem inventar uma categoria

**Pergunta:** quantos clientes têm cidade cadastrada e quantos estão sem cidade?

```sql
SELECT
  COUNT(*) AS total_clientes,
  COUNT(cidade) AS com_cidade,
  SUM(CASE WHEN cidade IS NULL THEN 1 ELSE 0 END) AS sem_cidade
FROM clientes;
```

Resultado: **4 clientes, 3 com cidade e 1 sem cidade**.

`COUNT(cidade)` ignora valores nulos. Para testar ausência, use `IS NULL`, não `= NULL`.

A base deste simulado representa ausência somente com `NULL`. Em um arquivo real, você pode encontrar `''`, espaços ou textos como “não informado”. Pergunte pela regra de limpeza: trocar tudo por “outras cidades” faz um problema de qualidade parecer uma localização conhecida.

## o erro que merece atenção: multiplicar valores no join

Imagine adicionar uma tabela de itens. O pedido 101 tem dois itens. Se você juntar pedido e item, o valor de **10000 centavos do pedido aparece duas vezes**. Somar o valor do pedido depois dessa junção produziria 20000 centavos só para ele.

Antes de cada `JOIN`, eu escreveria:

1. uma linha na tabela da esquerda representa o quê?
2. uma linha na tabela da direita representa o quê?
3. um registro da esquerda pode encontrar quantos registros da direita?
4. depois da junção, o que cada linha passa a representar?

Se a pergunta é total de pedidos, talvez você nem precise da tabela de itens. Se precisa de informação dos itens, pode agregá-los por pedido antes de juntar. A solução depende da pergunta.

**`SUM(DISTINCT valor_centavos)` não é um conserto geral.** Dois pedidos diferentes podem ter o mesmo valor. Remover valores repetidos eliminaria uma compra legítima. `DISTINCT` trata repetição do valor selecionado, não conhece a identidade do pedido por você.

## como explicar a consulta na entrevista

Não precisa narrar cada tecla. Explique regra, escolha e conferência:

> “vou considerar somente pedidos pagos em setembro. Como a pergunta pede todos os clientes, parto de clientes e uso left join. Filtro os pedidos no on para preservar quem não comprou. Conto o id do pedido para não contar a linha vazia. Depois confiro se o total dos clientes soma os mesmos 53000 centavos da consulta geral.”

Se travar, reduza o problema: primeiro liste os pedidos elegíveis, depois some, depois junte. Veja também o guia de [entrevista técnica de júnior](/carreira/entrevista-tecnica-junior/) para organizar essa comunicação.

Antes de uma seleção remota, confirme banco, editor, duração e se consulta à documentação é permitida. Pergunte também sobre uso de IA. Se a regra proíbe assistência, não use durante a avaliação; se permite, você continua responsável por entender e validar a resposta.

## transforme o treino em uma prova de aprendizado

No seu repositório, guarde `schema.sql`, `consultas.sql` e um README com as perguntas e os resultados esperados. Identifique claramente que é **um exercício com dados fictícios**, não um projeto feito para uma empresa.

Para sair da cópia, acrescente um cliente que comprou só em outubro, dois pedidos com o mesmo valor e um pedido no primeiro dia do mês seguinte. Registre qual resultado deve mudar e qual deve permanecer igual. Esse pequeno teste vale mais como evidência que um print de consulta sem contexto.

Se quiser um projeto mais completo, siga o guia de [portfólio de dados](/carreira/portfolio-dados-dev-junior/). O simulado treina correção de consultas; o portfólio precisa também de pergunta, origem dos dados, documentação e limites da conclusão.

## perguntas frequentes

### preciso decorar todas as funções de SQL?

Não. Priorize consultas básicas, junções, agregações e nulos. Aprenda a localizar uma função na documentação e a conferir seu comportamento. Durante a seleção, respeite a regra sobre consultas externas.

### esse simulado basta para uma vaga de engenharia de dados?

Não necessariamente. É uma revisão de SQL. A vaga pode pedir Python, modelagem, pipelines, testes de qualidade e infraestrutura. Use o anúncio para definir o próximo treino, não uma lista universal.

### posso aplicar antes de dominar funções de janela?

Pode comparar seu nível com o anúncio e aplicar de forma honesta. Se funções de janela forem requisito central, pratique também `ROW_NUMBER`, `RANK` e agregações com `OVER`. Este exercício não promete cobrir toda avaliação.

### como sei que acertei além de a query rodar?

Confira as linhas esperadas, a conta manual e os casos limites. Uma boa validação aqui é somar os totais por cliente e obter os mesmos 53000 centavos do exercício 2, mantendo os quatro clientes no exercício 3.

## checklist antes de encerrar o treino

- [ ] consigo recriar a base em um banco vazio;
- [ ] expliquei período, status e unidade monetária;
- [ ] sei por que o cancelamento e o pedido de outubro ficam de fora;
- [ ] mantenho Diego no relatório de todos os clientes;
- [ ] sei distinguir `COUNT(*)` de `COUNT(p.id)`;
- [ ] trato `NULL` sem fingir que conheço o dado ausente;
- [ ] verifico se uma junção muda a quantidade de linhas;
- [ ] consigo explicar a solução sem ler uma resposta decorada.

Terminou? Compare o que você praticou com as [vagas de dados júnior remoto](/vagas/dados-junior-remoto/). A lista pode incluir funções diferentes de dados: leia tarefas, nível e requisitos. Sua candidatura acontece no site da empresa; o eu.dev.br não envia a aplicação por você.
