O que você vai
saber fazer.
Objetivos da Aula
Reconhecer os cinco comandos SQL fundamentais — SELECT, FROM, WHERE, JOIN e GROUP BY — e seus propósitos básicos.
Explicar como cada cláusula SQL transforma os dados para responder uma pergunta de negócio específica.
Escrever consultas com SELECT, WHERE, JOIN e GROUP BY para extrair informações de um banco relacional normalizado.
Interpretar o resultado de uma consulta e diagnosticar por que uma query retorna dados errados ou gera erros de sintaxe.
Julgar se a resposta retornada por uma query — inclusive gerada por IA — responde corretamente a pergunta de negócio original.
Quem vai buscar
as respostas?
SQL é quase português — leia em voz alta
-- SELECIONE o nome dos artistas SELECT artista_nome -- DA tabela artistas FROM artistas -- ONDE o gênero seja MPB WHERE artista_genero = 'MPB';

SQL como alfabetização

SQL é quase português. Uma query pode ser lida em voz alta e compreendida por quem nunca programou — o ponto é esse.

Os 5 comandos do dia a dia

SELECT · FROM · WHERE · JOIN · GROUP BY resolvem 80% das perguntas analíticas de uma empresa. Os outros 20% são variações.

Humano valida, IA gera

A IA pode gerar código correto que responde a pergunta errada. A curadoria analítica ainda é humana.

Clientes inativos

Quais clientes não compram há mais de seis meses? Marketing precisa da lista para a campanha de reativação.

Artistas em crescimento

Quais artistas tiveram crescimento de receita nos últimos 12 meses? Curadoria precisa renovar contratos.

Ticket médio por país

Quais países têm o maior ticket médio? Expansão precisa definir mercados prioritários.

Do caos à consulta:
7 tabelas.
✗ Antes — 1 tabela, 23 colunas
  • pedido_id, pedido_data
  • cliente_id, cliente_nome, cliente_email, cliente_pais, cliente_cidade
  • artista_id, artista_nome, artista_genero, artista_pais_origem
  • faixa_id, faixa_titulo, album_id, album_titulo, album_ano, faixa_duracao_seg, faixa_preco_usd, faixa_formato
  • gravadora_id, gravadora_nome
  • qtd_comprada, total_pedido_usd

Anomalias de inserção, alteração e exclusão. Ana Lima com dois IDs. Preço zerado em 4 linhas.

✓ Depois — 7 tabelas relacionadas
  • clientes
  • artistas
  • gravadoras
  • albuns
  • faixas
  • pedidos
  • itens_pedido

Cada entidade em sua tabela. PKs e FKs garantem integridade. Preço centralizado em faixas.

Schema PostgreSQL — SoundByte
clientes
PK cliente_id
cliente_nome
cliente_email
cliente_pais
cliente_cidade
artistas
PK artista_id
artista_nome
artista_genero
artista_pais_origem
gravadoras
PK gravadora_id
gravadora_nome
albuns
PK album_id
album_titulo
album_ano
FK artista_id
FK gravadora_id
faixas
PK faixa_id
faixa_titulo
faixa_duracao_seg
faixa_preco_usd
faixa_formato
FK album_id
pedidos
PK pedido_id
pedido_data
FK cliente_id
itens_pedido
FK pedido_id
FK faixa_id
qtd_comprada
total_item_usd
DDL — Schema completo SoundByte (PostgreSQL)
CREATE TABLE clientes ( cliente_id SERIAL PRIMARY KEY, cliente_nome VARCHAR(100) NOT NULL, cliente_email VARCHAR(100) UNIQUE NOT NULL, cliente_pais VARCHAR(50), cliente_cidade VARCHAR(50) ); CREATE TABLE artistas ( artista_id SERIAL PRIMARY KEY, artista_nome VARCHAR(100) NOT NULL, artista_genero VARCHAR(50), artista_pais_origem VARCHAR(50) ); CREATE TABLE gravadoras ( gravadora_id SERIAL PRIMARY KEY, gravadora_nome VARCHAR(100) NOT NULL ); CREATE TABLE albuns ( album_id SERIAL PRIMARY KEY, album_titulo VARCHAR(150) NOT NULL, album_ano SMALLINT, artista_id INTEGER REFERENCES artistas(artista_id), gravadora_id INTEGER REFERENCES gravadoras(gravadora_id) ); CREATE TABLE faixas ( faixa_id SERIAL PRIMARY KEY, faixa_titulo VARCHAR(200) NOT NULL, faixa_duracao_seg INTEGER, faixa_preco_usd NUMERIC(6,2) NOT NULL, faixa_formato VARCHAR(10), album_id INTEGER REFERENCES albuns(album_id) ); CREATE TABLE pedidos ( pedido_id SERIAL PRIMARY KEY, pedido_data DATE NOT NULL, cliente_id INTEGER REFERENCES clientes(cliente_id) ); CREATE TABLE itens_pedido ( pedido_id INTEGER REFERENCES pedidos(pedido_id), faixa_id INTEGER REFERENCES faixas(faixa_id), qtd_comprada INTEGER DEFAULT 1, total_item_usd NUMERIC(6,2), PRIMARY KEY (pedido_id, faixa_id) );
DDL
Data Definition Language — comandos que criam e alteram a estrutura do banco (CREATE, ALTER, DROP).
DML
Data Manipulation Language — comandos que movimentam dados (INSERT, UPDATE, DELETE, SELECT).
REFERENCES
Declara a FK e delega ao SGBD a responsabilidade de garantir a integridade referencial automaticamente.
Escolha o quê
e de onde.
SELECT cliente_nome Quais colunas mostrar FROM clientes Qual tabela consultar RESULTADO Carlos Mendes Julia Ferreira Emma Clarke
Selecionar colunas específicas
SELECT cliente_nome, cliente_email, cliente_pais FROM clientes;
Resultado
cliente_nomecliente_emailcliente_pais
Carlos Mendescarlos@email.comBrasil
Julia Ferreirajulia@email.comBrasil
Emma Clarkeemma@email.comUK
Yuki Tanakayuki@email.comJapão
Selecionar colunas e ordenar por preço
SELECT faixa_titulo, faixa_preco_usd, faixa_formato FROM faixas ORDER BY faixa_preco_usd DESC;
Resultado — faixas mais caras primeiro
faixa_titulofaixa_preco_usdfaixa_formato
Milonga Infinita4.99MP3
Nacht Signal4.49FLAC
Saudade Eterna3.99MP3
Foggy Days3.99AAC
Teranga1.99MP3
SELECT *
O asterisco retorna todas as colunas. Útil para explorar, mas evite em produção — traz dados desnecessários e sobrecarrega o banco.
ORDER BY
Ordena o resultado. ASC (padrão, crescente) ou DESC (decrescente).
Alias com AS
SELECT faixa_preco_usd AS preco renomeia a coluna no resultado, útil para relatórios mais legíveis.
Filtre as linhas
que importam.
Tabela de origem — clientes
Todos os registros (sem filtro)
cliente_idcliente_nomecliente_emailcliente_pais
5001Carlos Mendescarlos@email.comBrasil
5002Julia Ferreirajulia@email.comBrasil
5003Yuki Tanakayuki@email.comJapão
5004Emma Clarkeemma@email.comUK
5005Ana Limaana@email.comBrasil
WHERE — filtrando por país
SELECT cliente_nome, cliente_email, cliente_pais FROM clientes WHERE cliente_pais = 'Brasil';
Resultado — apenas clientes do Brasil
cliente_nomecliente_emailcliente_pais
Carlos Mendescarlos@email.comBrasil
Julia Ferreirajulia@email.comBrasil
Ana Limaana@email.comBrasil
Combinando condições — AND, BETWEEN, LIKE
-- Faixas baratas em formato MP3 SELECT faixa_titulo, faixa_preco_usd, faixa_formato FROM faixas WHERE faixa_preco_usd BETWEEN 1.99 AND 3.99 AND faixa_formato = 'MP3' ORDER BY faixa_preco_usd DESC;
Operadores de comparação
= igual  ·  <> diferente  ·  > maior  ·  < menor  ·  >= <= maior/menor ou igual.
BETWEEN · LIKE · IN
BETWEEN a AND b — intervalo inclusivo  ·  LIKE '%texto%' — busca parcial  ·  IN (a, b, c) — lista de valores permitidos.
AND · OR · IS NULL
AND todas as condições devem ser verdadeiras · OR basta uma · IS NULL / IS NOT NULL verificam ausência de valor — campo vazio não é igual a zero.
Cruze tabelas
pela chave.
INNER JOIN TABLE1 TABLE2 Apenas registros comuns LEFT JOIN TABLE1 TABLE2 Todos da esquerda RIGHT JOIN TABLE1 TABLE2 Todos da direita FULL OUTER JOIN TABLE1 TABLE2 Todos, de ambas as tabelas
CLIENTES CLIENTE_ID NOME PAÍS PK 5001 Carlos Brasil 5002 Julia Brasil 5004 Emma UK JOIN cliente_id PEDIDOS PED_ID CLI_ID DATA TOTAL PK FK 1001 5001 2024-03-01 2.99 1002 5002 2024-03-02 6.98 1006 5001 2024-03-06 2.99
INNER JOIN — Pedidos com nome do cliente
SELECT p.pedido_id, p.pedido_data, c.cliente_nome, c.cliente_pais, ip.total_item_usd FROM pedidos p JOIN clientes c ON p.cliente_id = c.cliente_id JOIN itens_pedido ip ON p.pedido_id = ip.pedido_id WHERE p.pedido_data >= '2024-03-01' ORDER BY p.pedido_data;
LEFT JOIN — Clientes sem compra há 6 meses (PostgreSQL)
SELECT c.cliente_nome, c.cliente_email, MAX(p.pedido_data) AS ultima_compra FROM clientes c LEFT JOIN pedidos p ON c.cliente_id = p.cliente_id GROUP BY c.cliente_id, c.cliente_nome, c.cliente_email HAVING MAX(p.pedido_data) < CURRENT_DATE - INTERVAL '180 days' OR MAX(p.pedido_data) IS NULL ORDER BY ultima_compra ASC NULLS LAST;
INNER JOIN
Retorna apenas linhas com correspondência nas duas tabelas. É o JOIN padrão — equivale a cruzar duas planilhas pelo ID em comum.
LEFT JOIN
Retorna todas as linhas da tabela esquerda, mesmo sem correspondência na direita. Linhas sem match chegam com NULL — útil para detectar clientes sem pedidos.
NULLS LAST
PostgreSQL: ORDER BY x ASC NULLS LAST empurra os NULLs para o final — clientes sem nenhuma compra ficam no final da lista.

Considere as duas tabelas abaixo. Use-as para praticar JOIN no seu navegador.

CLIENTES
NUM_CLIENTENOME_CLIENTECIDADEESTADO
1001Jose AndradeRio de JaneiroRJ
1002Maria MendoncaCampinasSP
1003Marcio MotaCuritibaPR
1004Claudia SilvaSanta CatarinaSC
PEDIDOS
NUM_PEDIDONUM_CLIENTEDATA_PEDIDO
1010012020-03-10
2010022020-03-11
3010032020-03-11
Como fazer
  1. Acesse sqliteonline.com e selecione PGLite no painel lateral esquerdo.
  2. Copie o esquema SQL abaixo, cole no editor e clique em ▶ Run para criar as tabelas e inserir os dados.
  3. Escreva uma nova consulta com JOIN que retorne: número do pedido, nome do cliente, data do pedido e estado.
Esquema SQL — copie e execute no PGLite
-- Tabela de clientes CREATE TABLE CLIENTES ( Num_Cliente INTEGER PRIMARY KEY, Nome_Cliente VARCHAR(100), Cidade VARCHAR(100), Estado CHAR(2) ); -- Tabela de pedidos CREATE TABLE PEDIDOS ( Num_Pedido INTEGER PRIMARY KEY, Num_Cliente INTEGER, Data_Pedido DATE, FOREIGN KEY (Num_Cliente) REFERENCES CLIENTES(Num_Cliente) ); -- Inserindo dados na tabela CLIENTES INSERT INTO CLIENTES (Num_Cliente, Nome_Cliente, Cidade, Estado) VALUES (1001, 'Jose Andrade', 'Rio de Janeiro', 'RJ'), (1002, 'Maria Mendonca', 'Campinas', 'SP'), (1003, 'Marcio Mota', 'Curitiba', 'PR'), (1004, 'Claudia Silva', 'Santa Catarina', 'SC'); -- Inserindo dados na tabela PEDIDOS INSERT INTO PEDIDOS (Num_Pedido, Num_Cliente, Data_Pedido) VALUES (10, 1001, '2020-03-10'), (20, 1002, '2020-03-11'), (30, 1003, '2020-03-11');
🎯 Desafio
Escreva uma consulta JOIN que retorne uma tabela com as colunas:
Num_Pedido · Nome_Cliente · Data_Pedido · Estado
Agrupe para
enxergar padrões.
SEM GROUP BY PAÍS TOTAL Brasil 2.99 Brasil 6.98 UK 3.99 Brasil 2.99 GROUP BY cliente_pais COM GROUP BY PAÍS PEDIDOS TICKET MÉD. Brasil 18 4.12 UK 4 3.99 Japão 3 4.65
Respondendo a pergunta — Ticket médio por país
SELECT c.cliente_pais, COUNT(DISTINCT p.pedido_id) AS total_pedidos, ROUND(SUM(ip.total_item_usd), 2) AS receita_total, ROUND(AVG(ip.total_item_usd), 2) AS ticket_medio FROM clientes c JOIN pedidos p ON c.cliente_id = p.cliente_id JOIN itens_pedido ip ON p.pedido_id = ip.pedido_id WHERE c.cliente_pais IS NOT NULL GROUP BY c.cliente_pais ORDER BY ticket_medio DESC;
Resultado parcial
cliente_paistotal_pedidosreceita_totalticket_medio
Japão317.945.98
Alemanha211.465.73
Argentina29.984.99
Brasil1857.843.21
Funções de agregação
COUNT conta linhas · SUM soma · AVG média · MAX/MIN extremos. Sempre usadas com GROUP BY ou aplicadas a toda a tabela.
HAVING vs WHERE
WHERE filtra antes da agregação (linha por linha). HAVING filtra depois — é o WHERE dos grupos. Ex.: HAVING COUNT(*) > 5
A tabela dinâmica do SQL
GROUP BY é a SQL equivalente de arrastar um campo para "Linhas" no pivot e outro para "Valores". A diferença: é reproduzível, auditável e escalável.
A IA escreve.
Você valida.
O que a IA faz bem
  • Gerar queries sintaticamente corretas a partir de uma descrição em português
  • Identificar erros de sintaxe em queries quebradas
  • Sugerir índices e otimizações de performance
  • Explicar o que cada cláusula de uma query faz
  • Converter queries entre dialetos (PostgreSQL, MySQL, SQLite)
O que o humano deve validar
  • Se a query responde a pergunta de negócio certa — não apenas a pergunta literal
  • Se os JOINs estão duplicando ou perdendo linhas
  • Se o filtro de data usa o campo correto da tabela correta
  • Se o resultado faz sentido no contexto do negócio
  • Se há valores NULL afetando a média ou o total
Exemplo de bom prompt para a IA
Tenho um banco PostgreSQL com as tabelas: - clientes (cliente_id, cliente_nome, cliente_pais) - pedidos (pedido_id, cliente_id, pedido_data) - itens_pedido (pedido_id, faixa_id, total_item_usd) Escreva uma query que retorne os 5 países com maior ticket médio por pedido, excluindo clientes sem país cadastrado. Ordene do maior para o menor.
Regra de ouro: quanto mais contexto você dá à IA (nomes reais das tabelas, colunas existentes, o que a query deve responder), mais precisa e validável fica a resposta.
Query com erros — exercício em sala
-- Pergunta: Quais os artistas com maior receita em 2024? -- Esta query tem 3 erros. Use a IA para encontrá-los. SELECT ar.Name AS artista, SUM(il.UnitPrice * il.Quantity) AS receita FROM Artist ar JOIN Album al ON ar.ArtistId = al.ArtistId JOIN Track t ON al.AlbumId = t.AlbumId JOIN InvoiceLine il ON t.TrackId = il.TrackId WHERE il.InvoiceDate > '2024-01-01' -- Erro 1 GROUP BY ar.Name ORDERED BY receita DESC -- Erro 2 LIMIT 10; -- Erro 3 (lógico)
Erro 1 — Campo errado
InvoiceDate pertence à tabela Invoice, não a InvoiceLine. Falta o JOIN com Invoice na query.
Erro 2 — Sintaxe
ORDERED BY não existe em SQL. O comando correto é ORDER BY. O banco retorna erro imediatamente.
Erro 3 — Lógico
Sem o JOIN com Invoice, a query não filtra por data e retorna receita histórica completa, não apenas de 2024 — a resposta parece correta mas está errada.
Da IA ao
insight.
Exercício 1 — Banco de Dados Netflix
Com a ajuda da IA, formule perguntas analíticas sobre um banco de dados real e responda com SQL — do diagrama à consulta.
↓ netflixdb-postgres.sql SQLite Online ↗ dbdiagram ↗
1
Acesse o SQLite Online
Abra o navegador e acesse o ambiente de consulta em sqliteonline.com. Nenhuma instalação necessária.
2
Carregue o banco de dados Netflix
Importe o arquivo netflixdb-postgres.sql. Selecione o motor PGLite antes de carregar.
3
Crie perguntas analíticas com auxílio da IA
Crie pelo menos 3 perguntas analíticas focadas na tomada de decisão sobre este banco de dados, e diga por que cada uma é relevante. Responda cada uma com SQL.
4
Rode o script e salve os arquivos
Execute o SQL gerado no SQLite Online e salve dois arquivos: um .sql com as consultas de todas as perguntas, e um .xlsx com todas as respostas.

Passo 1 — Diagrama: crie um diagrama ERD do banco de dados usando o dbdiagram e salve o diagrama como figura PNG.

Passo 2 — Prompt: carregue o diagrama na IA e use o prompt abaixo.

Prompt sugerido
Eu tenho o banco de dados PostgreSQL da figura anexada. Proponha 3 perguntas analíticas e crie um script com consultas SQL para cada pergunta em um único arquivo, contendo as perguntas e seus insights nos comentários antes de cada consulta.
Entrega 1
Arquivo .sql contendo as consultas para todas as perguntas.
Entrega 2
Arquivo .xlsx contendo todas as respostas.
Responda com
dados.
SCHEMA CHINOOK — TABELAS PRINCIPAIS ARTIST ArtistId · Name ALBUM AlbumId · Title · ArtistId TRACK TrackId · Name AlbumId · UnitPrice INVOICELINE InvoiceLineId InvoiceId · TrackId INVOICE InvoiceId · CustomerId InvoiceDate · Total CUSTOMER CustomerId Country · Email
1
Acesse o SQLite Online
Abra o navegador e acesse o ambiente de consulta online. Nenhuma instalação necessária.
Abrir SQLite Online ↗
2
Carregue o dataset Chinook
Clique em File → Open DB e selecione o arquivo Chinook_Sqlite.sql fornecido pelo professor. O banco será carregado automaticamente com 11 tabelas e dados reais.
3
Execute as consultas dos exercícios abaixo
Cole cada query na área de texto, clique em Run e analise o resultado. Anote o que cada coluna representa e se a resposta faz sentido de negócio.
4
Exercício de IA: debug da query quebrada
Cole a "query com erros" da página anterior no ChatGPT com o prompt: "Esta query SQL tem erros. Identifique cada erro, explique o que está errado e mostre a versão corrigida." Compare a resposta com o gabarito do professor.
Exercícios — Chinook
Exercício 1
Quais são os 10 artistas com maior receita total de vendas?
Tabelas: ArtistAlbumTrackInvoiceLine · Função: SUM(UnitPrice * Quantity) · Ordenar: DESC
Exercício 2
Qual o ticket médio por país dos clientes? Ordene do maior para o menor.
Tabelas: CustomerInvoice · Função: AVG(Total) agrupado por Country · Excluir países com NULL
Exercício 3
Quais clientes não compraram após 2013? Liste nome, país e data da última compra.
Tabelas: CustomerInvoice · Condição: MAX(InvoiceDate) < '2014-01-01' com HAVING · Ordenar pela data mais antiga
Exercício 4 — Desafio
Para cada país, qual é o artista com maior número de faixas vendidas?
Requer: CustomerInvoiceInvoiceLineTrackAlbumArtist · Agrupe por Country e ArtistId · Use subquery ou CTE para pegar o top 1 por país
Query de referência — Exercício 1 (gabarito)
SELECT ar.Name AS artista, ROUND(SUM(il.UnitPrice * il.Quantity), 2) AS receita FROM Artist ar JOIN Album al ON ar.ArtistId = al.ArtistId JOIN Track t ON al.AlbumId = t.AlbumId JOIN InvoiceLine il ON t.TrackId = il.TrackId GROUP BY ar.ArtistId, ar.Name ORDER BY receita DESC LIMIT 10;
Chinook
Base de dados fictícia de uma loja de música digital. 275 artistas, 347 álbuns, 3.503 faixas e 412 clientes em 24 países. Estrutura similar ao Spotify no início.
SQLite Online
Ambiente SQL no navegador — sem instalação. Suporta SQLite com sintaxe muito similar ao PostgreSQL para os comandos desta aula.
Integração com IA
Ao pedir ajuda à IA, descreva as tabelas, as colunas e a pergunta de negócio. Quanto mais contexto, mais precisa e validável a query gerada.