Consulte um Banco de Filmes Real
Carregue um dataset real de 4.968 filmes no MySQL e responda perguntas reais com ele: filtre, ordene, junte pessoas a filmes, agregue notas e use subqueries. O complemento prático do curso SQL Essentials.
Problema
Ler sobre `SELECT` não é o mesmo que consultar um banco que revida — com NULLs, milhões de linhas em tabelas relacionadas e perguntas que não cabem num único comando. Neste lab você vai trabalhar com o **dataset de filmes**: quase 5.000 filmes reais, 8.000 pessoas, suas reviews (notas do IMDb, votos) e papéis (quem dirigiu e atuou em quê). Você vai subir o schema, importar os CSVs e então responder seis perguntas reais — do tipo que um time de produto ou de dados de fato faria — usando exatamente as habilidades do curso SQL Essentials: `WHERE`, `ORDER BY`, `JOIN`, `GROUP BY`, `HAVING` e subqueries. No fim você terá escrito queries contra um dataset real e bagunçado e produzido um `queries.sql` que serve de prova de que você sabe transformar uma pergunta em SQL.
Objetivos
- Criar o schema e importar um dataset CSV multi-tabela no MySQL na ordem certa (respeitando as foreign keys)
- Filtrar e ordenar com
WHERE,AND/OR,IN,BETWEENeORDER BY, tratandoNULLcorretamente - Combinar tabelas com
INNER JOINentrefilms,reviews,rolesepeople - Resumir dados com
GROUP BY,COUNT,AVG,MIN/MAXe arredondar os resultados - Filtrar grupos com
HAVINGe responder perguntas de comparação com subqueries - Empacotar suas respostas num deliverable reproduzível
queries.sql
Pré-requisitos
- MySQL 8 instalado e rodando (MySQL Community Server; MySQL Workbench opcional, mas útil para o assistente de importação). Rode
mysql --versionpara confirmar. - O download Dataset de Filmes da DARE Labs (o
films-dataset.zipcomschema.sqle os quatro CSVs:films,people,reviews,roles). - Um terminal e um cliente SQL (a CLI
mysqlou o Workbench) - Git instalado, para entregar seu
queries.sqlno fim - Ter concluído (ou ter familiaridade com) o curso SQL Essentials — este lab assume que você sabe o que
SELECT/JOIN/GROUP BYfazem
O que você vai construir
Você não vai construir um app — vai construir fluência. Partindo de CSVs crus, você vai subir
um banco relacional real e então escrever seis queries que respondem perguntas reais sobre filmes:
os filmes mais bem avaliados, os diretores mais prolíficos, a nota média por país e mais. Cada
passo empilha mais uma habilidade de SQL sobre a anterior.
O dataset em uma imagem
Quatro tabelas, conectadas por IDs:
| Tabela | Linhas | O que guarda | Colunas-chave |
|---|---|---|---|
films |
4.968 | uma linha por filme |
id, title, release_year, country, language, duration, gross, budget
|
people |
8.397 | diretores e atores |
id, name, birthdate
|
reviews |
4.968 | uma linha com as notas de cada filme |
film_id → films, imdb_score, num_votes
|
roles |
19.791 | quem fez o quê num filme |
film_id → films, person_id → people, role (director ou actor) |
Os relacionamentos são todo o ponto de um banco relacional:
- um film tem uma linha em reviews (
reviews.film_id = films.id) - um film tem muitos roles, e cada role aponta para uma person (
roles.person_id = people.id)
Para responder "qual diretor tem a maior nota média no IMDb" você precisa viajar people → roles → films → reviews. Esse caminho é para o que servem os joins.
Um aviso sobre dados reais: NULLs
Isso é dado real, então é incompleto. gross, budget, language e birthdate estão faltando
(NULL) em muitas linhas. Não é um bug — é o estado normal de dados de produção, e tratá-lo
corretamente (com IS NOT NULL, e sabendo que AVG ignora NULLs) é exatamente a habilidade que
este lab treina.
Como trabalhar neste lab
Rode cada query você mesmo contra sua própria cópia do banco e leia o resultado real. Salve cada
query final num arquivo chamado queries.sql à medida que avança — esse arquivo é seu deliverable
no Passo 6.
Passos
-
Suba o banco e importe os CSVs
Antes de consultar qualquer coisa, você precisa dos dados no MySQL.
Crie o schema
Descompacte o
films-dataset.zipe rode oschema.sqlincluído. Pela CLImysql:mysql -u root -p < schema.sqlIsso cria o banco
filmse as quatro tabelas com suas chaves primárias e estrangeiras. As
foreign keys fazem a ordem de importação importar: uma linha derolesreferencia uma de
filmse uma depeople, então essas precisam existir antes. Importe nesta ordem:
films → people → reviews → roles.Importe os dados
Escolha um método:
Opção A — LOAD DATA (rápido, CLI). Habilite o local infile e carregue cada arquivo:
USE films; LOAD DATA LOCAL INFILE 'films.csv' INTO TABLE films FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 LINES; LOAD DATA LOCAL INFILE 'people.csv' INTO TABLE people FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 LINES; LOAD DATA LOCAL INFILE 'reviews.csv' INTO TABLE reviews FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 LINES; LOAD DATA LOCAL INFILE 'roles.csv' INTO TABLE roles FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 LINES;(Se der
ERROR 3948, inicie o cliente commysql --local-infile=1e rode
SET GLOBAL local_infile = 1;.)Opção B — assistente do Workbench. Botão direito em cada tabela → Table Data Import Wizard
→ escolha o CSV correspondente. Faça na ordem films → people → reviews → roles.Verifique a carga
Nunca confie numa importação que você não conferiu. Confirme as contagens:
SELECT (SELECT COUNT(*) FROM films) AS films, (SELECT COUNT(*) FROM people) AS people, (SELECT COUNT(*) FROM reviews) AS reviews, (SELECT COUNT(*) FROM roles) AS roles;Você deve ver 4968, 8397, 4968, 19791. Se alguma contagem estiver errada, reimporte aquela tabela.
Entregável deste passo: o resultado das contagens provando que as quatro tabelas carregaram.
-
Filtre e ordene: WHERE e ORDER BY
Comece com uma única tabela,
films, e responda perguntas estreitando e ordenando linhas.Pergunta 1 — os filmes recentes mais longos
Liste os 10 filmes mais longos lançados de 2000 em diante, do mais longo primeiro, mostrando título, ano e duração.
SELECT title, release_year, duration FROM films WHERE release_year >= 2000 AND duration IS NOT NULL ORDER BY duration DESC LIMIT 10;Leia o que cada cláusula faz:
-
WHERE release_year >= 2000mantém só filmes recentes. -
AND duration IS NOT NULLdescarta linhas sem duração — crítico, porque senão o
ORDER BYordenaria os NULLs junto e te enganaria. NULL é "desconhecido", não zero. -
ORDER BY duration DESCpõe o mais longo primeiro;LIMIT 10pega a fatia do topo.
Experimente as variações
Pratique os outros operadores de filtro do curso:
-- IN: filmes de um conjunto de países SELECT title, country FROM films WHERE country IN ('Brazil', 'France', 'Japan') ORDER BY country, title; -- BETWEEN: filmes dos anos 1990 SELECT title, release_year FROM films WHERE release_year BETWEEN 1990 AND 1999 ORDER BY release_year; -- LIKE: filmes cujo título começa com "The Lord" SELECT title FROM films WHERE title LIKE 'The Lord%';Entregável deste passo: salve a query da Pergunta 1 e seu resultado top-10 no
queries.sql. -
-
Conecte tabelas: INNER JOIN
A nota de um filme mora em
reviews, não emfilms. Para usar as duas, você junta (join) elas pela chave compartilhada.Pergunta 2 — os filmes mais bem avaliados
Mostre os 10 filmes mais bem avaliados (título, ano, nota IMDb), mas só os com peso real de
audiência — ao menos 50.000 votos — para que um único voto 10/10 não lidere a lista.SELECT f.title, f.release_year, r.imdb_score, r.num_votes FROM films AS f INNER JOIN reviews AS r ON r.film_id = f.id WHERE r.num_votes >= 50000 ORDER BY r.imdb_score DESC, r.num_votes DESC LIMIT 10;As ideias-chave:
-
INNER JOIN reviews AS r ON r.film_id = f.idcola cada filme à sua linha de review pela foreign key. -
Aliases de tabela (
f,r) encurtam a query e deixamf.titlevsr.imdb_scoresem ambiguidade. -
INNERsignifica que só aparecem linhas que casam dos dois lados — filmes sem linha de review somem. - O filtro
num_votes >= 50000é um instinto real de analista: proteja-se de ruído de amostra pequena.
Junte três tabelas — quem dirigiu
Para trazer o nome do diretor, viaje
films → roles → people:SELECT f.title, p.name AS director, r.imdb_score FROM films AS f INNER JOIN reviews AS r ON r.film_id = f.id INNER JOIN roles AS rl ON rl.film_id = f.id AND rl.role = 'director' INNER JOIN people AS p ON p.id = rl.person_id WHERE r.num_votes >= 50000 ORDER BY r.imdb_score DESC LIMIT 10;Repare na condição de join
rl.role = 'director'— sem ela você casaria também todo ator,
multiplicando as linhas. Filtrar dentro do join mantém o grão em "uma linha por filme".Entregável deste passo: salve a query da Pergunta 2 e seu resultado no
queries.sql. -
-
Resuma com GROUP BY e agregações
Agregação colapsa muitas linhas numa linha-resumo por grupo. É aqui que o SQL responde
"quantos", "em média", "o maior".Pergunta 3 — nota média por país
Para cada país, mostre quantos filmes avaliados ele tem e sua nota IMDb média — só países
com ao menos 30 filmes avaliados, melhor média primeiro.SELECT f.country, COUNT(*) AS rated_films, ROUND(AVG(r.imdb_score), 2) AS avg_score FROM films AS f INNER JOIN reviews AS r ON r.film_id = f.id WHERE f.country IS NOT NULL GROUP BY f.country HAVING COUNT(*) >= 30 ORDER BY avg_score DESC;A mecânica que confunde:
-
GROUP BY f.countryfaz uma linha de saída por país distinto; as agregações descrevem cada grupo. - Toda coluna não-agregada no
SELECTprecisa aparecer noGROUP BY(aqui,f.country). -
AVG(r.imdb_score)ignora notas NULL automaticamente;ROUND(..., 2)deixa legível. -
HAVING COUNT(*) >= 30filtra grupos — você não pode usarWHEREpara isso, porque o
WHEREroda antes do agrupamento.WHEREfiltra linhas;HAVINGfiltra grupos. Essa
distinção é uma pergunta clássica de entrevista.
Pergunta 4 — os diretores mais prolíficos
Quem dirigiu mais filmes? Mostre os 10 diretores com mais filmes.
SELECT p.name AS director, COUNT(*) AS films_directed FROM roles AS rl INNER JOIN people AS p ON p.id = rl.person_id WHERE rl.role = 'director' GROUP BY p.id, p.name ORDER BY films_directed DESC LIMIT 10;Agrupar por
p.id(não sóp.name) é o hábito seguro — duas pessoas diferentes podem ter o
mesmo nome, e o id as mantém distintas.Entregável deste passo: salve as Perguntas 3 e 4 com seus resultados no
queries.sql. -
-
Responda perguntas de comparação com subqueries
Algumas perguntas comparam cada linha a um valor que você precisa calcular antes. Esse cálculo
interno é uma subquery.Pergunta 5 — filmes acima da média geral
Quantos filmes têm nota acima da nota IMDb média do dataset inteiro?
SELECT COUNT(*) AS above_average_films FROM reviews WHERE imdb_score > (SELECT AVG(imdb_score) FROM reviews);O
(SELECT AVG(imdb_score) FROM reviews)entre parênteses roda primeiro e retorna um único
número — a média global. A query externa então compara a nota de cada filme com ele. Você não
poderia escreverWHERE imdb_score > AVG(imdb_score)direto, porque um agregado não pode ficar
numWHEREsobre as mesmas linhas — a subquery é como você contorna isso.Pergunta 6 — diretores que superam a média
Liste diretores cuja nota média de filmes está acima da média geral, com sua média e
contagem de filmes — mais filmes primeiro. (Ao menos 5 filmes, para ter significado.)SELECT p.name AS director, COUNT(*) AS films, ROUND(AVG(r.imdb_score), 2) AS avg_score FROM roles AS rl INNER JOIN people AS p ON p.id = rl.person_id INNER JOIN reviews AS r ON r.film_id = rl.film_id WHERE rl.role = 'director' GROUP BY p.id, p.name HAVING COUNT(*) >= 5 AND AVG(r.imdb_score) > (SELECT AVG(imdb_score) FROM reviews) ORDER BY films DESC, avg_score DESC;Esta única query usa tudo: um join de três tabelas,
GROUP BY, dois agregados, umHAVINGque
combina um filtro de contagem com uma subquery, e umORDER BYde múltiplas chaves. Se você
consegue lê-la e explicar cada cláusula, você domina o núcleo do SQL.Entregável deste passo: salve as Perguntas 5 e 6 com seus resultados no
queries.sql. -
Entregue: seu queries.sql
Empacote as seis queries num único arquivo reproduzível e entregue.
Monte sua entrega
Crie um arquivo
queries.sqlcom suas seis respostas, cada uma sob um comentário de cabeçalho,
com o resultado colado abaixo como comentário. Estrutura:-- Lab do Dataset de Filmes — <seu nome> -- Q1: 10 filmes mais longos lançados de 2000 em diante SELECT title, release_year, duration FROM films WHERE release_year >= 2000 AND duration IS NOT NULL ORDER BY duration DESC LIMIT 10; /* Resultado (top 3): title | release_year | duration ... */ -- Q2: top 10 filmes mais bem avaliados com >= 50000 votos -- ... query + resultado ... -- Q3: nota IMDb média por país (>= 30 filmes avaliados) -- Q4: top 10 diretores mais prolíficos -- Q5: quantos filmes têm nota acima da média geral -- Q6: diretores cuja média supera a média geral (>= 5 filmes)Entregue
git init git add queries.sql git commit -m "Lab de SQL — queries do dataset de filmes" # faça push para um repo público ou gist, depois entregue essa URLEntregue a URL do repositório ou gist como seu deliverable do lab.
Critério de submissão (autoverificação)
- A checagem de contagem do Passo 1 mostra 4968 / 8397 / 4968 / 19791
- As seis queries rodam sem erro numa importação nova do dataset
- Q2 e Q6 usam
JOINentre pelo menos três tabelas corretamente - Q3 usa
GROUP BY+HAVING(nãoWHERE) para filtrar grupos - Q5 e Q6 usam uma subquery para a comparação com a média
- Cada query tem seu resultado real colado abaixo (não inventado)
O que vem a seguir
Você consultou um banco relacional real de ponta a ponta. No Backend Developer Path, isso
vira a camada de dados de uma API que você constrói e faz deploy — as mesmas tabelasfilms,
agora atrás de endpoints HTTP que você projeta.