🗄️ 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


Sumário

Configuração

  1. Configurando seu ambiente personalizado
  2. Inserindo seus dados nas tabelas

Parte I — Múltipla Escolha (Q1 a Q10)

Parte II — Relacione e Complete (Q11 a Q15)

Parte III — Questões Abertas (Q16 a Q20)


1. Configurando seu Ambiente Personalizado

Passo 1 · Abrir o MySQL Workbench

  1. Inicie o MySQL Workbench pelo Menu Iniciar ou pelo atalho na área de trabalho.
  2. Clique na sua conexão local — geralmente chamada Local instance MySQL.
  3. 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
@ra Seu número de RA completo '20261234'
@nome_completo Seu nome completo 'Ana Lima'
@cidade Sua cidade natal 'Assis'
@salario Um número entre 2000 e 9000 (use os 4 últimos dígitos do RA) 1234.00

💡 O @salario baseado 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 USE via 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?


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?


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?


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?


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?


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?


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 JOIN nativo — emula-se com LEFT 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?


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?


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?


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?


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:

  1. Ocultação de dados sensíveis — a VIEW expõe só o necessário.
  2. Abstração da estrutura — mudanças internas não quebram relatórios.
  3. Controle granular de acessoSELECT na VIEW sem nenhum privilégio nas tabelas base.
  4. Redução de riscos — sem acesso direto, não há DELETE ou DROP acidentais.

🖥️ 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_gasto do 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!