🗄️ Guia Prático — MySQL Workbench
Vamos colocar a mão na massa! Reprodução das 20 Questões da Prova
Disciplina: Banco de Dados e Aplicações — Prova 2
Curso: Gestão da Tecnologia da Informação Formato: Atividade Individual · Banco Personalizado
Olá! 👋 Este guia foi preparado para te acompanhar passo a passo na execução de todas as 20 questões da prova diretamente no MySQL Workbench. Cada questão tem teoria explicada, SQL pronto para rodar e o gabarito comentado.
💡 Dica antes de começar
- Siga os passos na ordem — cada um prepara o terreno para o próximo.
- Seu nome e RA estarão dentro dos dados. Os resultados das suas consultas serão únicos — não há como copiar de outro colega.
- Copie cada bloco SQL exatamente como mostrado e ajuste somente onde houver indicação
← SUBSTITUA.- Se algo não sair como esperado, respire fundo e revise o passo anterior — errar faz parte do aprendizado! 😊
Sumário
Configuração
Parte I — Múltipla Escolha (Q1 a Q10)
- Q1 — LEFT JOIN: e os clientes sem pedido?
- Q2 — HAVING: filtrando após o agrupamento
- Q3 — Operador IN: elegância na filtragem
- Q4 — ORDER BY: qual é o padrão?
- Q5 — AVG(): calculando médias como um profissional
- Q6 — LIKE: buscando por padrão de texto
- Q7 — INNER JOIN vs FULL OUTER JOIN
- Q8 — DISTINCT: eliminando duplicatas
- Q9 — EXISTS: presença ou ausência?
- Q10 — CREATE VIEW: a tabela que não ocupa espaço
Parte II — Relacione e Complete (Q11 a Q15)
- Q11 — Relacione: COUNT, SUM e MAX
- Q12 — Complete: operador de faixa de valores
- Q13 — Complete: agrupar linhas idênticas
- Q14 — Complete: tabela referenciando a si mesma
- Q15 — Complete: curingas do LIKE
Parte III — Questões Abertas (Q16 a Q20)
- Q16 — Por que WHERE não funciona com COUNT()?
- Q17 — INNER JOIN vs RIGHT JOIN na prática
- Q18 — O que é uma Subquery Correlacionada?
- Q19 — VIEW como escudo de segurança
- Q20 — Desafio: query elaborada completa
1. Configurando seu Ambiente Personalizado
Passo 1 · Abrir o MySQL Workbench
- Inicie o MySQL Workbench pelo Menu Iniciar ou pelo atalho na área de trabalho.
- Clique na sua conexão local — geralmente chamada Local instance MySQL.
- Quando a área de trabalho SQL aparecer, você está pronto! ✅
Passo 2 · Abrir uma nova aba de consulta
Ctrl + T
Passo 3 · Criar o banco e as variáveis de sessão
✏️ Substitua apenas estas 4 linhas — o restante do guia usa as variáveis automaticamente:
Variável O que colocar Exemplo @raSeu número de RA completo '20261234'@nome_completoSeu nome completo 'Ana Lima'@cidadeSua cidade natal 'Assis'@salarioUm número entre 2000 e 9000 (use os 4 últimos dígitos do RA) 1234.00💡 O
@salariobaseado no RA garante que a média calculada na Q5 seja diferente para cada aluno!
⚠️ Este passo é dividido em duas execuções separadas — o MySQL não suporta o comando
USEvia SQL dinâmico. Siga exatamente como indicado.
Execução A — crie o banco (selecione só este bloco e execute com Ctrl + Shift + Enter):
-- ============================================================
-- BLOCO A: defina suas variáveis e crie o banco ← SUBSTITUA
-- ============================================================
SET @ra = '20261234'; -- ← SEU RA
SET @nome_completo = 'Ana Lima'; -- ← SEU NOME COMPLETO
SET @cidade = 'Assis'; -- ← SUA CIDADE NATAL
SET @salario = 1234.00; -- ← 4 últimos dígitos do RA como decimal
-- Monta e executa o CREATE DATABASE dinamicamente
SET @primeiro_nome = SUBSTRING_INDEX(@nome_completo, ' ', 1);
SET @schema_name = CONCAT('db_', @ra, '_', LOWER(@primeiro_nome));
SET @create_cmd = CONCAT('CREATE DATABASE IF NOT EXISTS `', @schema_name, '`');
PREPARE stmt FROM @create_cmd;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Mostra o nome exato do banco para você copiar no próximo passo:
SELECT CONCAT('USE `', @schema_name, '`;') AS COPIE_E_EXECUTE_ABAIXO;
Execução B — selecione o banco — copie o resultado da linha acima e execute:
-- Cole aqui o resultado exato da consulta anterior. Exemplo:
USE `db_20261234_ana`;
💡 Após executar o
USE, o nome do banco aparecerá em negrito no painel SCHEMAS à esquerda — confirmando que está selecionado.
Passo 4 · Confirmar que deu certo
No painel SCHEMAS (esquerda), clique em atualizar (seta circular). Seu banco deve aparecer na lista e ficar em negrito após o USE.
2. Inserindo seus Dados nas Tabelas
⚠️ Importante: execute este bloco inteiro de uma vez. Ele usa as variáveis que você definiu no Passo 3 — seu nome e RA entrarão automaticamente nos dados, tornando seus resultados únicos.
-- ============================================================
-- CRIAÇÃO DAS TABELAS
-- ============================================================
CREATE TABLE IF NOT EXISTS CLIENTE (
id INT NOT NULL AUTO_INCREMENT,
nome VARCHAR(100) NOT NULL,
cidade VARCHAR(80),
ra VARCHAR(20), -- ← identifica o dono do registro
PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS PEDIDO (
id INT NOT NULL AUTO_INCREMENT,
data DATE NOT NULL,
valor DECIMAL(10,2) NOT NULL,
fk_cliente INT,
PRIMARY KEY (id),
FOREIGN KEY (fk_cliente) REFERENCES CLIENTE(id)
);
CREATE TABLE IF NOT EXISTS FUNCIONARIO (
id INT NOT NULL AUTO_INCREMENT,
nome VARCHAR(100) NOT NULL,
cargo VARCHAR(60),
salario DECIMAL(10,2) NOT NULL,
id_gerente INT,
ra VARCHAR(20), -- ← identifica o dono do registro
PRIMARY KEY (id),
FOREIGN KEY (id_gerente) REFERENCES FUNCIONARIO(id)
);
CREATE TABLE IF NOT EXISTS PRODUTO (
id INT NOT NULL AUTO_INCREMENT,
nome VARCHAR(100) NOT NULL,
status ENUM('Ativo','Inativo','Pendente') NOT NULL,
preco DECIMAL(10,2),
PRIMARY KEY (id)
);
-- ============================================================
-- CLIENTES FIXOS (base comum a todos os alunos)
-- ============================================================
INSERT INTO CLIENTE (nome, cidade, ra) VALUES
('Carlos Silva', 'São Paulo', 'base'),
('Maria Oliveira', 'Campinas', 'base'),
('João Santos', 'Ribeirão Preto', 'base'),
('Pedro Nunes', 'Sorocaba', 'base'); -- sem pedidos: útil para Q1 e Q7
-- ============================================================
-- CLIENTE DO ALUNO ← seu nome e cidade entram aqui!
-- ============================================================
INSERT INTO CLIENTE (nome, cidade, ra)
VALUES (@nome_completo, @cidade, @ra);
-- ============================================================
-- PEDIDOS FIXOS (para os 4 clientes base)
-- ============================================================
INSERT INTO PEDIDO (data, valor, fk_cliente) VALUES
('2024-01-15', 1500.00, 1),
('2024-02-20', 3200.00, 1),
('2024-01-10', 800.00, 2),
('2024-03-05', 6100.00, 2),
('2024-02-28', 2200.00, 3),
('2024-04-12', 5500.00, 3);
-- Pedro Nunes (id=4) propositalmente sem pedidos → Q1 e Q7
-- O aluno (id=5) receberá pedidos personalizados abaixo
-- ============================================================
-- PEDIDOS DO ALUNO ← valores calculados a partir do RA
-- ============================================================
INSERT INTO PEDIDO (data, valor, fk_cliente) VALUES
('2024-03-10', @salario * 2.5, 5), -- pedido 1 do aluno
('2024-04-22', @salario * 1.8, 5), -- pedido 2 do aluno
('2024-05-15', @salario * 3.2, 5); -- pedido 3 do aluno
-- ============================================================
-- FUNCIONÁRIOS: gerente fixo + aluno como funcionário
-- ============================================================
INSERT INTO FUNCIONARIO (nome, cargo, salario, id_gerente, ra) VALUES
('Camila Rocha', 'Gerente', 9800.00, NULL, 'base'),
('Roberto Alves', 'Desenvolvedor', 6200.00, 1, 'base'),
('Diego Martins', 'Analista', 4800.00, 1, 'base'),
('Fernanda Costa', 'DBA', 7300.00, 1, 'base');
-- O aluno entra como funcionário com salário derivado do RA
INSERT INTO FUNCIONARIO (nome, cargo, salario, id_gerente, ra)
VALUES (@nome_completo, 'Analista', @salario + 2000.00, 1, @ra);
-- ============================================================
-- PRODUTOS (base comum + produto nomeado com inicial do aluno)
-- ============================================================
INSERT INTO PRODUTO (nome, status, preco) VALUES
('Teclado Mecânico', 'Ativo', 299.90),
('Monitor 24"', 'Inativo', 1299.00),
('Mouse Gamer', 'Ativo', 189.90),
('Headset USB', 'Pendente', 349.00),
('Webcam Full HD', 'Inativo', 249.50),
('Hub USB-C', 'Pendente', 99.90),
('SSD 1TB', 'Ativo', 459.00),
('Roteador Wi-Fi', 'Ativo', 389.90),
('RAM 16GB', 'Pendente', 229.90);
-- Produto personalizado: começa com a inicial do nome do aluno
-- (usado na Q6 — LIKE com primeira letra)
SET @produto_aluno = CONCAT(UPPER(LEFT(@nome_completo, 1)),
'it — Licença do Aluno ', @ra);
INSERT INTO PRODUTO (nome, status, preco)
VALUES (@produto_aluno, 'Ativo', @salario * 1.5);
-- ============================================================
-- CONFIRMAR OS DADOS DO ALUNO INSERIDOS
-- ============================================================
SELECT '=== SEUS DADOS NAS TABELAS ===' AS '';
SELECT id, nome, cidade, ra FROM CLIENTE WHERE ra = @ra;
SELECT id, nome, cargo, salario, ra FROM FUNCIONARIO WHERE ra = @ra;
SELECT id, nome, status, preco FROM PRODUTO
WHERE nome LIKE CONCAT(UPPER(LEFT(@nome_completo, 1)), '%');
Ao final, você verá uma confirmação com seu nome, cidade e RA nas tabelas. Se não aparecer, revise o Passo 3. 🔍
PARTE I — Múltipla Escolha
Q1 · LEFT JOIN — E os clientes sem pedido?
Gabarito: alternativa C
📚 Entendendo antes de executar
O LEFT JOIN traz todos os registros da tabela da esquerda (CLIENTE), mesmo sem correspondência na direita (PEDIDO). Quando não há correspondência, os campos da direita aparecem como NULL.
🖥️ Agora é com você
-- Q1: seu nome aparece no resultado com seus próprios pedidos
-- Pedro Nunes aparece com NULL — sem nenhum pedido
SELECT
c.nome AS nome_cliente,
c.cidade,
p.id AS pedido_id,
p.data AS data_pedido,
p.valor AS valor_pedido
FROM CLIENTE c
LEFT JOIN PEDIDO p ON p.fk_cliente = c.id
ORDER BY c.nome;
✅ O que você deve ver
Seu nome aparecerá 3 vezes (um para cada pedido personalizado) e Pedro Nunes aparecerá com NULL em todas as colunas de pedido.
| nome_cliente | cidade | pedido_id | data_pedido | valor_pedido |
|---|---|---|---|---|
| … | … | … | … | … |
| [SEU NOME] | [SUA CIDADE] | 7 | 2024-03-10 | [RA × 2.5] |
| [SEU NOME] | [SUA CIDADE] | 8 | 2024-04-22 | [RA × 1.8] |
| [SEU NOME] | [SUA CIDADE] | 9 | 2024-05-15 | [RA × 3.2] |
| Pedro Nunes | Sorocaba | NULL | NULL | NULL |
🎯 Assinatura do aluno: seu nome e cidade aparecem nas linhas com seus pedidos. O print desta tela identifica você.
💡 Por que as outras alternativas estão erradas?
- A — errada: LEFT JOIN preserva todos da esquerda — ninguém é omitido.
- B — errada: não há violação de integridade nessa operação.
- C ✅ — correta: clientes sem pedido aparecem com campos de PEDIDO como NULL.
- D — errada: não existe duplicação de registros nesse comportamento.
Q2 · HAVING — Filtrando após o agrupamento
Gabarito: alternativa B
📚 Entendendo antes de executar
WHERE filtra linhas antes do agrupamento — não enxerga funções de agregação. HAVING filtra depois do GROUP BY, quando COUNT() e SUM() já existem.
🖥️ Agora é com você
-- Q2: HAVING filtra APÓS o GROUP BY
-- Você deve aparecer no resultado, pois tem 3 pedidos!
SELECT
c.nome AS cliente,
c.cidade,
COUNT(p.id) AS total_pedidos
FROM CLIENTE c
INNER JOIN PEDIDO p ON p.fk_cliente = c.id
GROUP BY c.id, c.nome, c.cidade
HAVING COUNT(p.id) > 1
ORDER BY total_pedidos DESC, c.nome;
-- Tente trocar HAVING por WHERE e veja o erro:
-- WHERE COUNT(p.id) > 1 ← MySQL vai reclamar! Faça o teste.
✅ O que você deve ver
Seu nome aparece no topo com 3 pedidos — mais do que qualquer cliente base.
| cliente | cidade | total_pedidos |
|---|---|---|
| [SEU NOME] | [SUA CIDADE] | 3 |
| Carlos Silva | São Paulo | 2 |
| João Santos | Ribeirão Preto | 2 |
| Maria Oliveira | Campinas | 2 |
🎯 Assinatura do aluno: você será o único com 3 pedidos — seu nome ficará no topo da lista ordenada.
💡 Por que as outras alternativas estão erradas?
- A (
WHERE) — errada: avaliado antes do GROUP BY, não enxerga COUNT(). - B (
HAVING) ✅ — correta: avaliado após o agrupamento, quando COUNT() já existe. - C (
GROUP BY) — errada: agrupa, mas não filtra. - D (
ORDER BY) — errada: apenas define a ordem de exibição.
Q3 · Operador IN — Elegância na filtragem
Gabarito: alternativa C
📚 Entendendo antes de executar
O IN testa se um valor pertence a uma lista. É equivalente a múltiplos OR, mas muito mais legível — especialmente quando a lista cresce.
🖥️ Agora é com você
-- Q3: IN filtrando por lista de status
-- Seu produto personalizado (status 'Ativo') deve aparecer!
SELECT
id,
nome,
status,
preco
FROM PRODUTO
WHERE status IN ('Ativo', 'Pendente')
ORDER BY status, nome;
✅ O que você deve ver
Seu produto (que começa com a inicial do seu nome e termina com seu RA) aparece entre os Ativo. Nenhum outro aluno terá esse produto — é o seu!
| nome | status | preco |
|---|---|---|
| … | Ativo | … |
| [INICIAL]it — Licença do Aluno [RA] | Ativo | [RA × 1.5] |
| … | … | … |
🎯 Assinatura do aluno: o produto com seu RA no nome é exclusivo seu.
💡 Por que as outras alternativas estão erradas?
- A (
LIKE) — errada: para padrões de texto, não listas exatas. - B (
BETWEEN) — errada: para faixas numéricas contínuas. - C (
IN) ✅ — correta: perfeito para listas discretas de valores possíveis. - D (
EXISTS) — errada: verifica existência de linhas em subquery, uso diferente.
Q4 · ORDER BY — Qual é o padrão?
Gabarito: alternativa C
📚 Entendendo antes de executar
Sem especificar a direção, o MySQL adota ASC (crescente) automaticamente. Seu nome aparecerá em uma posição que depende da ordem alfabética — diferente para cada aluno.
🖥️ Agora é com você
-- Q4: seu nome aparece em posição alfabética única
-- Execute as três variações e compare
-- Variação 1: sem especificar (padrão = ASC)
SELECT nome, cidade FROM CLIENTE ORDER BY nome;
-- Variação 2: ASC explícito — resultado idêntico
SELECT nome, cidade FROM CLIENTE ORDER BY nome ASC;
-- Variação 3: DESC — tudo invertido
SELECT nome, cidade FROM CLIENTE ORDER BY nome DESC;
✅ O que você deve ver
Seu nome ocupará uma posição diferente dependendo de onde cai alfabeticamente. Nenhum aluno com nome diferente terá o mesmo resultado.
| nome (ASC — padrão) | cidade |
|---|---|
| (posição depende do seu nome) | (sua cidade) |
| Carlos Silva | São Paulo |
| João Santos | Ribeirão Preto |
| Maria Oliveira | Campinas |
| Pedro Nunes | Sorocaba |
🎯 Assinatura do aluno: a posição do seu nome na lista ordenada é única para cada aluno.
💡 Por que as outras alternativas estão erradas?
- A (
DESC) — errada: decrescente precisa ser declarado explicitamente. - B (
RAND) — errada: ordem aleatória, exigiriaORDER BY RAND(). - C (
ASC) ✅ — correta: padrão silencioso do MySQL. - D (por inserção) — errada: nenhum banco garante essa ordem sem instrução explícita.
Q5 · Função AVG() — Calculando médias como um profissional
Gabarito: alternativa B
📚 Entendendo antes de executar
AVG() calcula a média aritmética, somando todos os valores e dividindo pela quantidade de registros. Como seu salário é derivado do RA, a média da turma será diferente para cada configuração.
| Função | O que faz |
|---|---|
AVG() |
Média aritmética |
MAX() |
Maior valor |
MIN() |
Menor valor |
SUM() |
Soma total |
COUNT() |
Contagem de linhas |
🖥️ Agora é com você
-- Q5: sua entrada muda a média geral da tabela!
SELECT
COUNT(*) AS total_funcionarios,
MIN(salario) AS menor_salario,
MAX(salario) AS maior_salario,
SUM(salario) AS folha_total,
ROUND(AVG(salario), 2) AS media_salarial
FROM FUNCIONARIO;
-- Veja seu salário e o quanto ele influencia a média:
SELECT nome, cargo, salario, ra
FROM FUNCIONARIO
ORDER BY salario DESC;
✅ O que você deve ver
A média salarial será única para cada aluno porque seu salário (@salario + 2000) entra no cálculo. Um aluno com RA 20261234 terá salário 3234.00; outro com RA 20260001 terá 2001.00.
| nome | cargo | salario | ra |
|---|---|---|---|
| Camila Rocha | Gerente | 9800.00 | base |
| Fernanda Costa | DBA | 7300.00 | base |
| Roberto Alves | Desenvolvedor | 6200.00 | base |
| Diego Martins | Analista | 4800.00 | base |
| [SEU NOME] | Analista | [RA + 2000] | [SEU RA] |
🎯 Assinatura do aluno: a média final da tabela varia conforme o RA de cada aluno.
💡 Por que as outras alternativas estão erradas?
- A (
MAX()) — errada: retorna apenas o maior valor. - B (
AVG()) ✅ — correta: soma e divide pelo número de registros. - C (
SUM()) — errada: soma tudo, não calcula média. - D (
COUNT()) — errada: conta linhas, não opera com valores numéricos.
Q6 · LIKE — Buscando por padrão de texto
Gabarito: alternativa C
📚 Entendendo antes de executar
O LIKE usa curingas para buscar padrões em texto. Para nomes que começam com uma letra, o % fica no final: 'R%'.
| Curinga | Substitui | Exemplo |
|---|---|---|
% |
Zero ou mais caracteres | 'R%' → começa com R |
_ |
Exatamente 1 caractere | 'R_M' → R + 1 char + M |
🖥️ Agora é com você
-- Q6: busque produtos que começam com a inicial do SEU NOME
-- Seu produto personalizado deve aparecer aqui!
-- Substitua 'A' pela inicial do seu nome:
SELECT nome, status, preco
FROM PRODUTO
WHERE nome LIKE 'A%' -- ← troque A pela sua inicial
ORDER BY nome;
-- Para comparar, veja todos os produtos e suas iniciais:
SELECT nome, LEFT(nome, 1) AS inicial, status, preco
FROM PRODUTO
ORDER BY nome;
✅ O que você deve ver
Ao buscar pela inicial do seu nome, seu produto personalizado aparece — e provavelmente nenhum outro aluno terá produto com a mesma inicial e o mesmo RA.
| nome | status | preco |
|---|---|---|
| [INICIAL]it — Licença do Aluno [RA] | Ativo | [RA × 1.5] |
| (outros produtos com a mesma inicial, se houver) | … | … |
🎯 Assinatura do aluno: o produto com seu RA aparece exclusivamente na sua busca com LIKE.
💡 Por que as outras alternativas estão erradas?
- A (
'%R') — errada:%no início significa termina com R, não começa. - B (
'R') — errada: busca exatamente a string “R” — não encontraria nada. - C (
'R%') ✅ — correta: começa com R e pode ter qualquer coisa depois. - D (
'*R') — errada:*não é curinga válido no SQL.
Q7 · INNER JOIN vs FULL OUTER JOIN
Gabarito: alternativa A
📚 Entendendo antes de executar
| Tipo | O que retorna |
|---|---|
INNER JOIN |
Apenas registros com correspondência em ambas as tabelas |
LEFT JOIN |
Todos da esquerda + correspondências da direita |
RIGHT JOIN |
Todos da direita + correspondências da esquerda |
FULL OUTER JOIN |
Todos de ambas, com ou sem correspondência |
⚠️ MySQL não tem
FULL OUTER JOINnativo — emula-se comLEFT JOIN UNION RIGHT JOIN.
🖥️ Agora é com você
-- Q7a: INNER JOIN — Pedro Nunes NÃO aparece (sem pedidos)
-- Seu nome aparece 3 vezes (seus 3 pedidos)
SELECT c.nome, c.cidade, p.id AS pedido_id, p.valor
FROM CLIENTE c
INNER JOIN PEDIDO p ON p.fk_cliente = c.id
ORDER BY c.nome;
-- Q7b: FULL OUTER JOIN emulado — Pedro Nunes aparece com NULL
SELECT c.nome, c.cidade, p.id AS pedido_id, p.valor
FROM CLIENTE c
LEFT JOIN PEDIDO p ON p.fk_cliente = c.id
UNION
SELECT c.nome, c.cidade, p.id AS pedido_id, p.valor
FROM CLIENTE c
RIGHT JOIN PEDIDO p ON p.fk_cliente = c.id
ORDER BY nome;
✅ O que você deve ver
INNER JOIN: seu nome aparece 3 vezes, Pedro Nunes não aparece.
FULL OUTER JOIN: Pedro Nunes aparece com NULL, seu nome ainda aparece 3 vezes.
🎯 Assinatura do aluno: a quantidade de linhas com seu nome (3) é proporcional ao seu RA — diferente de colegas com RAs de dígitos finais diferentes que geram valores distintos.
💡 Por que as outras alternativas estão erradas?
- A ✅ — correta: INNER JOIN retorna apenas registros com correspondência em ambas.
- B — errada: quem traz tudo é o FULL OUTER JOIN.
- C — errada: INNER JOIN não bloqueia tabelas.
- D — errada: INNER JOIN funciona com qualquer tipo de chave.
Q8 · DISTINCT — Eliminando duplicatas
Gabarito: alternativa B
📚 Entendendo antes de executar
DISTINCT instrui o MySQL a retornar apenas valores únicos — elimina linhas repetidas do resultado. Sua cidade natal pode ou não coincidir com cidades de outros clientes, tornando o resultado único.
🖥️ Agora é com você
-- Q8: compare com e sem DISTINCT
-- Sua cidade natal pode ser nova na lista!
-- Sem DISTINCT: cidades se repetem se houver mais de um cliente por cidade
SELECT cidade FROM CLIENTE ORDER BY cidade;
-- Com DISTINCT: cada cidade aparece uma única vez
SELECT DISTINCT cidade FROM CLIENTE ORDER BY cidade;
-- Para produtos: quais status existem?
SELECT DISTINCT status FROM PRODUTO ORDER BY status;
✅ O que você deve ver
Se sua cidade natal for diferente de São Paulo, Campinas, Ribeirão Preto e Sorocaba, ela aparece como cidade nova e única na lista. Com DISTINCT, cada cidade só aparece uma vez independente de quantos clientes a compartilham.
🎯 Assinatura do aluno: se sua cidade for única, ela só aparece na sua lista — identificando seu resultado.
💡 Por que as outras alternativas estão erradas?
- A — errada: DISTINCT não tem relação com índices físicos de disco.
- B ✅ — correta: elimina linhas duplicadas do conjunto de resultados.
- C — errada: DISTINCT não ordena; para isso existe
ORDER BY. - D — errada: DISTINCT não interfere em restrições de Foreign Key.
Q9 · EXISTS — Presença ou ausência?
Gabarito: alternativa B
📚 Entendendo antes de executar
EXISTS não se importa com quais valores a subquery retorna — apenas verifica se ela retorna pelo menos uma linha. É uma pergunta de sim ou não: “existe algum registro que satisfaça essa condição?”
🖥️ Agora é com você
-- Q9: EXISTS verifica se o cliente tem pelo menos um pedido
-- Você deve aparecer! Pedro Nunes não deve aparecer.
SELECT c.nome, c.cidade
FROM CLIENTE c
WHERE EXISTS (
SELECT 1
FROM PEDIDO p
WHERE p.fk_cliente = c.id
)
ORDER BY c.nome;
-- Compare: NOT EXISTS → quem NÃO tem pedidos
SELECT c.nome, c.cidade
FROM CLIENTE c
WHERE NOT EXISTS (
SELECT 1 FROM PEDIDO p WHERE p.fk_cliente = c.id
);
✅ O que você deve ver
Com EXISTS: seu nome aparece (você tem 3 pedidos). Pedro Nunes não aparece.
Com NOT EXISTS: apenas Pedro Nunes.
🎯 Assinatura do aluno: seu nome e cidade aparecem no resultado de EXISTS — identificando sua execução.
💡 Por que as outras alternativas estão erradas?
- A — errada: EXISTS ignora o valor retornado; só importa se há ou não há linha.
- B ✅ — correta: avalia presença ou ausência de pelo menos uma linha.
- C — errada: EXISTS não tem relação com ordenação alfabética.
- D — errada: campos multivalorados não fazem parte da lógica do EXISTS.
Q10 · CREATE VIEW — A tabela que não ocupa espaço
Gabarito: alternativa B
📚 Entendendo antes de executar
Uma VIEW é um atalho nomeado para uma consulta. Não armazena dados — cada acesso executa a query original por baixo dos panos. É uma tabela virtual que simplifica consultas e controla o que cada usuário vê.
🖥️ Agora é com você
-- Q10: criando uma VIEW que inclui seus dados automaticamente
CREATE OR REPLACE VIEW vw_resumo_clientes AS
SELECT
c.nome AS cliente,
c.cidade,
c.ra,
COUNT(p.id) AS total_pedidos,
ROUND(SUM(p.valor), 2) AS total_gasto
FROM CLIENTE c
LEFT JOIN PEDIDO p ON p.fk_cliente = c.id
GROUP BY c.id, c.nome, c.cidade, c.ra;
-- Consultando a VIEW — seu nome aparece com seu total único:
SELECT * FROM vw_resumo_clientes ORDER BY total_gasto DESC;
-- Views existentes no banco:
SHOW FULL TABLES WHERE Table_type = 'VIEW';
✅ O que você deve ver
Seu nome e RA aparecem na VIEW com seu total de gastos — calculado a partir dos seus pedidos personalizados. Nenhum outro aluno terá o mesmo total_gasto.
| cliente | cidade | ra | total_pedidos | total_gasto |
|---|---|---|---|---|
| [SEU NOME] | [SUA CIDADE] | [SEU RA] | 3 | [único] |
| João Santos | Ribeirão Preto | base | 2 | 7700.00 |
| … | … | … | … | … |
| Pedro Nunes | Sorocaba | base | 0 | NULL |
🎯 Assinatura do aluno: seu
total_gastoé calculado com base no RA — diferente para cada aluno.
💡 Por que as outras alternativas estão erradas?
- A — errada: VIEW não duplica dados nem ocupa espaço adicional.
- B ✅ — correta: query armazenada no catálogo que funciona como tabela virtual.
- C — errada: VIEW não é backup e não usa JSON.
- D — errada: VIEW não cria gráficos.
PARTE II — Relacione e Complete
Q11 · Relacione: COUNT, SUM e MAX
Gabarito: C → MAX · A → COUNT · B → SUM
🖥️ Veja as três em ação — com seus dados incluídos
-- Q11: as três funções com dados reais da tabela que inclui você
SELECT
COUNT(id) AS "COUNT → total de funcionários",
ROUND(SUM(salario), 2) AS "SUM → folha de pagamento total",
MAX(salario) AS "MAX → maior salário"
FROM FUNCIONARIO;
-- Veja também por cargo:
SELECT
cargo,
COUNT(*) AS quantidade,
ROUND(SUM(salario), 2) AS soma_salarios,
MAX(salario) AS maior_salario
FROM FUNCIONARIO
GROUP BY cargo
ORDER BY soma_salarios DESC;
✅ O que você deve ver
O COUNT incluirá você como o 5º funcionário. O SUM e MAX também refletirão seu salário derivado do RA.
Q12 · Complete: operador de faixa de valores
Resposta: BETWEEN
📚 Explicação
BETWEEN filtra valores dentro de uma faixa inclusiva, substituindo >= AND <=.
🖥️ Agora é com você
-- Q12: BETWEEN aplicado ao preço dos produtos
-- Versão verbosa (equivalente):
SELECT nome, preco FROM PRODUTO WHERE preco >= 200.00 AND preco <= 500.00 ORDER BY preco;
-- Versão elegante com BETWEEN:
SELECT nome, status, preco
FROM PRODUTO
WHERE preco BETWEEN 200.00 AND 500.00
ORDER BY preco;
-- Seu produto personalizado pode entrar nessa faixa dependendo do RA!
-- Verifique: seu preço é @salario * 1.5
SELECT nome, preco,
CASE WHEN preco BETWEEN 200.00 AND 500.00
THEN '✅ Dentro da faixa'
ELSE '❌ Fora da faixa'
END AS faixa
FROM PRODUTO
ORDER BY preco;
🎯 Assinatura do aluno: dependendo do seu RA, seu produto pode ou não entrar na faixa — o resultado do
CASEé diferente para cada aluno.
Q13 · Complete: agrupar linhas idênticas
Resposta: GROUP BY
📚 Explicação
GROUP BY agrupa linhas com mesmo valor em colunas específicas, permitindo aplicar funções de agregação sobre cada grupo.
🖥️ Agora é com você
-- Q13: GROUP BY agrupando funcionários por cargo
-- Você estará no grupo 'Analista'
SELECT
cargo,
COUNT(*) AS quantidade,
ROUND(AVG(salario), 2) AS salario_medio,
MAX(salario) AS maior_salario
FROM FUNCIONARIO
GROUP BY cargo
ORDER BY salario_medio DESC;
-- Confirme em qual grupo você está:
SELECT nome, cargo, salario
FROM FUNCIONARIO
WHERE cargo = 'Analista'
ORDER BY salario DESC;
✅ O que você deve ver
No grupo Analista, seu nome aparece com seu salário ao lado de Diego Martins — e a média do grupo muda conforme o RA de cada aluno.
🎯 Assinatura do aluno: a média salarial do grupo Analista é diferente para cada aluno.
Q14 · Complete: tabela referenciando a si mesma
Resposta: Self Join
📚 Explicação
Um Self Join une uma tabela a si mesma com dois aliases diferentes. Usado para hierarquias internas, como descobrir o gerente de cada funcionário dentro da mesma tabela.
🖥️ Agora é com você
-- Q14: Self Join — seu nome aparece com Camila Rocha como gerente
SELECT
f.nome AS funcionario,
f.cargo,
f.salario,
g.nome AS nome_gerente
FROM FUNCIONARIO f
LEFT JOIN FUNCIONARIO g ON g.id = f.id_gerente
ORDER BY g.nome NULLS FIRST, f.nome;
✅ O que você deve ver
Seu nome aparece como funcionário subordinado à Camila Rocha (gerente). A coluna salario com seu valor derivado do RA torna este resultado único.
| funcionario | cargo | salario | nome_gerente |
|---|---|---|---|
| Camila Rocha | Gerente | 9800.00 | NULL |
| Diego Martins | Analista | 4800.00 | Camila Rocha |
| Fernanda Costa | DBA | 7300.00 | Camila Rocha |
| Roberto Alves | Desenvolvedor | 6200.00 | Camila Rocha |
| [SEU NOME] | Analista | [RA+2000] | Camila Rocha |
🎯 Assinatura do aluno: seu nome e salário aparecem na hierarquia — único para cada RA.
Q15 · Complete: curingas do LIKE
Resposta: _ (underscore) e % (porcentagem)
📚 Explicação
| Curinga | Substitui | Exemplo | Encontra |
|---|---|---|---|
_ |
Exatamente 1 caractere | '__D%' |
“SSD 1TB” |
% |
Zero ou mais caracteres | '%USB%' |
“Headset USB”, “Hub USB-C” |
🖥️ Agora é com você
-- Q15: testando os dois curingas com os produtos da tabela
-- _ substitui exatamente 1 caractere:
SELECT nome FROM PRODUTO WHERE nome LIKE 'S_D%'; -- S + 1 char + D...
-- % substitui qualquer sequência:
SELECT nome FROM PRODUTO WHERE nome LIKE '%USB%'; -- contém USB
-- Seu produto com a inicial do nome:
-- Substitua 'A' pela sua inicial
SELECT nome, preco FROM PRODUTO WHERE nome LIKE 'A%'; -- ← troque A
-- Combinando _ e %: 3ª letra é 'M'
SELECT nome FROM PRODUTO WHERE nome LIKE '__M%';
🎯 Assinatura do aluno: ao buscar pela inicial do seu nome, seu produto personalizado aparece — exclusivo para cada aluno.
PARTE III — Questões Abertas
Q16 · Por que WHERE não funciona com COUNT()?
📚 Gabarito comentado
WHERE é avaliado antes do GROUP BY — quando COUNT() ainda não existe. HAVING é avaliado depois, quando os valores já foram calculados.
Ordem de execução do MySQL:
1. FROM → define as tabelas
2. WHERE → filtra linhas individuais ← COUNT() ainda não existe aqui!
3. GROUP BY → agrupa as linhas
4. HAVING → filtra os grupos ← COUNT() já existe aqui ✅
5. SELECT → projeta as colunas
6. ORDER BY → ordena o resultado
🖥️ Comprove — e veja seu nome no resultado correto
-- Q16: HAVING é a solução correta
-- Você deve aparecer (tem 3 pedidos — mais que qualquer base)
SELECT
c.nome AS cliente,
c.cidade,
c.ra,
COUNT(p.id) AS total_pedidos
FROM CLIENTE c
INNER JOIN PEDIDO p ON p.fk_cliente = c.id
GROUP BY c.id, c.nome, c.cidade, c.ra
HAVING COUNT(p.id) >= 2
ORDER BY total_pedidos DESC;
-- Isso gera ERRO — experimente e documente o erro no print:
-- WHERE COUNT(p.id) >= 2 ← "Invalid use of group function"
🎯 Assinatura do aluno: seu nome aparece no topo com 3 pedidos, enquanto os demais têm 2.
Q17 · INNER JOIN vs RIGHT JOIN na prática
📚 Gabarito comentado
| Tipo | O que retorna |
|---|---|
INNER JOIN |
Apenas registros com correspondência em ambas as tabelas |
RIGHT JOIN |
Todos da direita + correspondências da esquerda (sem correspondência → NULL na esquerda) |
🖥️ Comprove — com seus pedidos personalizados
-- Q17a: INNER JOIN — Pedro Nunes não aparece; você aparece 3 vezes
SELECT c.nome, c.cidade, c.ra, p.valor
FROM CLIENTE c
INNER JOIN PEDIDO p ON p.fk_cliente = c.id
ORDER BY c.nome, p.valor;
-- Q17b: RIGHT JOIN — todos os pedidos aparecem,
-- mesmo que o cliente seja excluído futuramente
SELECT c.nome, c.cidade, p.id AS pedido_id, p.valor
FROM CLIENTE c
RIGHT JOIN PEDIDO p ON p.fk_cliente = c.id
ORDER BY p.id;
🎯 Assinatura do aluno: seus 3 pedidos com valores derivados do RA aparecem em ambas as consultas, tornando o resultado único.
Q18 · O que é uma Subquery Correlacionada?
📚 Gabarito comentado
Uma Subquery Correlacionada referencia colunas da consulta externa e é reexecutada para cada linha da consulta principal. Em tabelas grandes isso causa lentidão grave.
🖥️ Comprove — você aparece no resultado da correlacionada
-- Q18: Subquery CORRELACIONADA — reexecuta para cada cliente
SELECT c.nome, c.cidade, c.ra
FROM CLIENTE c
WHERE EXISTS (
SELECT 1
FROM PEDIDO p
WHERE p.fk_cliente = c.id -- ← c.id vem da consulta externa!
);
-- Alternativa performática com JOIN (mesmo resultado, mais eficiente):
SELECT DISTINCT c.nome, c.cidade, c.ra
FROM CLIENTE c
INNER JOIN PEDIDO p ON p.fk_cliente = c.id
ORDER BY c.nome;
🎯 Assinatura do aluno: seu nome e cidade aparecem em ambas as consultas — o ra confirma que é você.
Q19 · VIEW como escudo de segurança
📚 Gabarito comentado
Dar acesso a uma VIEW em vez das tabelas físicas oferece:
- Ocultação de dados sensíveis — a VIEW expõe só o necessário.
- Abstração da estrutura — mudanças internas não quebram relatórios.
- Controle granular de acesso —
SELECTna VIEW sem nenhum privilégio nas tabelas base. - Redução de riscos — sem acesso direto, não há
DELETEouDROPacidentais.
🖥️ Comprove — VIEW que expõe apenas o necessário
-- Q19: VIEW de relatório que oculta dados sensíveis
-- mas exibe seu nome e RA para identificação
CREATE OR REPLACE VIEW vw_relatorio_seguro AS
SELECT
c.nome AS cliente,
c.cidade,
c.ra, -- identificação do aluno
COUNT(p.id) AS total_pedidos,
ROUND(SUM(p.valor), 2) AS total_gasto
-- campos como cpf, senha, dados_bancarios ficariam OCULTOS aqui
FROM CLIENTE c
LEFT JOIN PEDIDO p ON p.fk_cliente = c.id
GROUP BY c.id, c.nome, c.cidade, c.ra;
-- Usuário de relatório vê apenas isso — sem acesso às tabelas reais:
SELECT * FROM vw_relatorio_seguro
WHERE ra = '20261234' -- ← substitua pelo seu RA para ver só os seus dados
ORDER BY total_gasto DESC;
🎯 Assinatura do aluno: filtrando a VIEW pelo seu RA, apenas seus dados aparecem — a VIEW serve como camada de segurança e identificação ao mesmo tempo.
Q20 · Desafio: Query Elaborada Completa
Chegou a hora de unir tudo! Esta é a questão que combina mais conceitos — e seu resultado será completamente diferente do de qualquer colega, porque seus pedidos têm valores únicos derivados do RA. 💪
📋 Enunciado
Retorne o nome do cliente e o valor total gasto em pedidos. Filtre apenas clientes com total acima de R$ 5.000,00, ordenando do maior para o menor.
🖥️ Agora é com você
-- Q20: query completa — JOIN + SUM + GROUP BY + HAVING + ORDER BY
SELECT
c.nome AS nome_cliente,
c.cidade,
c.ra,
SUM(p.valor) AS total_gasto
FROM CLIENTE c
INNER JOIN PEDIDO p ON p.fk_cliente = c.id
GROUP BY c.id, c.nome, c.cidade, c.ra
HAVING SUM(p.valor) > 5000.00
ORDER BY total_gasto DESC;
✅ O que você deve ver
Seu nome e total de gastos serão únicos. Um aluno com RA 20261234 tem pedidos de 1234×2.5 = 3085, 1234×1.8 = 2221.20 e 1234×3.2 = 3948.80 — totalizando 9255.00. Já um aluno com RA 20260500 totaliza 3750.00 — e nem entra no resultado (abaixo de 5000)!
| nome_cliente | cidade | ra | total_gasto |
|---|---|---|---|
| [SEU NOME] | [SUA CIDADE] | [SEU RA] | [único por RA] |
| João Santos | Ribeirão Preto | base | 7700.00 |
| Maria Oliveira | Campinas | base | 6900.00 |
🎯 Assinatura do aluno: o
total_gastodo aluno é matematicamente derivado do RA — impossível de ser copiado.
💡 O papel de cada peça
| Cláusula | Por que está aqui |
|---|---|
INNER JOIN |
Conecta cada pedido ao seu cliente |
GROUP BY |
Agrupa todos os pedidos de um mesmo cliente |
SUM(p.valor) |
Soma os valores dentro de cada grupo |
HAVING > 5000 |
Filtra após o agrupamento — só quem passou de R$ 5.000 |
ORDER BY DESC |
Maior gasto no topo |
Resumo Geral — todas as 20 questões
Parte I — Múltipla Escolha
| Q | Conceito | Gabarito | Assinatura do aluno |
|---|---|---|---|
| 1 | LEFT JOIN | C | Seu nome + cidade aparecem com 3 pedidos; Pedro Nunes com NULL |
| 2 | HAVING | B | Você fica no topo com 3 pedidos vs 2 dos outros |
| 3 | Operador IN | C | Seu produto com RA no nome aparece entre os Ativos |
| 4 | ORDER BY | C | Seu nome ocupa posição alfabética única na lista |
| 5 | AVG() | B | Seu salário (RA+2000) altera a média geral da tabela |
| 6 | LIKE | C | Seu produto aparece ao buscar pela inicial do seu nome |
| 7 | INNER JOIN | A | Seus 3 pedidos únicos aparecem no JOIN |
| 8 | DISTINCT | B | Sua cidade pode ser nova e única na lista |
| 9 | EXISTS | B | Seu nome + cidade confirmam sua execução |
| 10 | CREATE VIEW | B | Seu total_gasto na VIEW é único por RA |
Parte II — Relacione e Complete
| Q | Resposta | Assinatura do aluno |
|---|---|---|
| 11 | C→MAX · A→COUNT · B→SUM | Seu salário entra no COUNT e SUM |
| 12 | BETWEEN |
Seu produto pode ou não entrar na faixa — depende do RA |
| 13 | GROUP BY |
Média do grupo Analista muda com seu salário |
| 14 | Self Join |
Seu nome aparece na hierarquia com salário único |
| 15 | _ e % |
Busca pela sua inicial encontra seu produto exclusivo |
Parte III — Questões Abertas
| Q | Tema | Assinatura do aluno |
|---|---|---|
| 16 | WHERE vs HAVING | Você aparece no topo com 3 pedidos |
| 17 | INNER vs RIGHT JOIN | Seus 3 pedidos únicos identificam você |
| 18 | Subquery Correlacionada | Seu nome + ra confirmam a execução |
| 19 | VIEW e segurança | Filtrar pelo seu RA retorna só seus dados |
| 20 | Query elaborada | total_gasto calculado matematicamente pelo RA |
🏆 Parabéns por chegar até aqui!