Doze módulos interativos para dominar os fundamentos de programação — do zero, com exemplos reais e práticas ao vivo.
Organizar código pensando no mundo real
OOP é uma forma de organizar código modelando o mundo real. Em vez de uma lista de instruções, você cria objetos — entidades com características e comportamentos próprios.
Torna código mais organizado, reutilizável e fácil de manter. Você cria um molde uma vez e usa quantas vezes quiser.
Molde vs. instância real
Uma classe é a definição — descreve quais atributos (dados) e métodos (ações) os objetos terão. A classe em si não é um objeto; é só a ideia.
Um objeto é criado a partir de uma classe. Pode ter infinitos objetos da mesma classe — cada um com seus próprios valores.
# Definindo a CLASSE (o molde) class Cachorro: def __init__(self, nome, raca, idade): self.nome = nome self.raca = raca self.idade = idade def latir(self): return f"{self.nome} diz: Au au!" # Criando OBJETOS (instâncias) rex = Cachorro("Rex", "Labrador", 3) bolinha = Cachorro("Bolinha", "Poodle", 7) print(rex.latir()) # → Rex diz: Au au!
Clique em cada pilar para expandir
Esconder dados internos
Reutilizar de outra classe
Mesma ação, resultado diferente
Simplificar a complexidade
# HERANÇA + POLIMORFISMO class Animal: def __init__(self, nome): self.nome = nome def falar(self): return "..." class Cachorro(Animal): # herda de Animal def falar(self): # polimorfismo return "Au au!" class Gato(Animal): def falar(self): return "Miau!" animais = [Cachorro("Rex"), Gato("Mimi")] for a in animais: print(a.nome, ":", a.falar()) # Rex : Au au! / Mimi : Miau!
Crie objetos da classe Carro ao vivo
class Carro: def __init__(self, nome, cor, ano, tipo): self.nome = nome self.cor = cor self.ano = ano self.tipo = tipo # Seus objetos aparecerão aqui ↓
Caixas que guardam informação na memória
const quando o valor
não muda e let quando
pode mudar.
Uma caixa com etiqueta. A etiqueta é o nome, dentro fica o valor. O programa pode ler ou trocar o que está dentro.
const nome = "Ana" // String const idade = 25 // Number const aprovado = true // Boolean let pontos = 0 pontos = 10 // ✅ let permite reatribuir // const nome = "Outro" ❌ erro!
Fazer o programa tomar decisões
Condicionais permitem que o programa escolha caminhos diferentes. Se algo for verdadeiro, faz X. Senão, faz Y.
const idade = 17 if (idade >= 18) { console.log("Pode votar ✅") } else if (idade >= 16) { console.log("Voto facultativo 🗳️") } else { console.log("Não pode votar ❌") } // → "Voto facultativo 🗳️" // === igual !== diferente // && E || OU
Executar código múltiplas vezes automaticamente
Imprimir 1 a 100 sem laço = 100 linhas. Com laço = 3 linhas. Laços automatizam repetição.
// FOR — sabe quantas vezes for (let i = 1; i <= 5; i++) { console.log(`Volta ${i}`) } // WHILE — não sabe quantas vezes let energia = 10 while (energia > 0) { energia -= 3 } // FOR...OF — percorre arrays const frutas = ["🍎", "🍌", "🍇"] for (const f of frutas) console.log(f)
Blocos de código reutilizáveis com nome
Um bloco de código com nome que você chama quantas vezes quiser. Recebe parâmetros e pode devolver um resultado (return).
// Declaração clássica function somar(a, b) { return a + b } console.log(somar(3, 4)) // → 7 // Arrow function (forma moderna) const dobrar = (n) => n * 2 console.log(dobrar(7)) // → 14
Listas ordenadas de valores — a estrutura mais usada em JS
Um array é uma lista ordenada de valores guardados em sequência. Cada item tem um índice numérico que começa em 0 — não em 1!
0, não no 1. O
primeiro item é
arr[0]!
// Criando um array const frutas = ["🍎", "🍌", "🍇", "🍓"] // Acessando por índice (começa em 0!) console.log(frutas[0]) // → "🍎" console.log(frutas[2]) // → "🍇" // Propriedades úteis console.log(frutas.length) // → 4 console.log(frutas[frutas.length-1]) // → último: "🍓" // Modificando frutas[1] = "🥭" // troca "🍌" por "🥭"
As ferramentas mais poderosas do JS moderno
Os métodos de array são usados em todo projeto real — filtrar dados de uma API, transformar listas, calcular totais. São o pão e a manteiga do JavaScript.
const nums = [1, 2, 3, 4, 5, 6] // map → transforma cada item nums.map(n => n * 2) // → [2,4,6,8,10,12] // filter → filtra por condição nums.filter(n => n % 2 === 0) // → [2,4,6] // reduce → acumula em um valor nums.reduce((acc, n) => acc + n, 0) // → 21 // Encadeando (chaining)! nums .filter(n => n > 2) .map(n => n * 10) // → [30,40,50,60]
Dados estruturados com chave e valor
Um objeto é uma coleção de pares chave:valor. Diferente de arrays (que usam índices numéricos), objetos usam nomes descritivos para acessar os dados.
const usuario = { nome: "Ana Silva", idade: 28, ehDev: true, skills: ["JS", "Python", "CSS"] } // Acessar propriedades usuario.nome // → "Ana Silva" usuario["idade"] // → 28 // Destructuring — extrai de uma vez! const { nome, idade } = usuario console.log(nome) // → "Ana Silva" // Spread — copia e adiciona campos const atualizado = { ...usuario, cidade: "SP" }
Monte seu próprio objeto JavaScript ao vivo
// Preencha os campos acima ↑
O problema do código que "trava enquanto espera"
O JS executa uma coisa de cada vez, em sequência. Isso é ótimo para simplicidade — mas cria um problema: e quando uma operação demora? Buscar dados de uma API pode levar 2 segundos. Sem async, o programa inteiro travaria esperando.
// ❌ SÍNCRONO — trava tudo! console.log("1. Início") const dados = buscarDados() // espera 3s → trava! console.log("2. Dados:", dados) console.log("3. Fim") // ✅ ASSÍNCRONO — não trava! console.log("1. Início") buscarDados().then(dados => { console.log("3. Dados:", dados) // chega depois }) console.log("2. Fim") // executa ANTES dos dados! // Saída: "1. Início" → "2. Fim" → "3. Dados"
Uma promessa de que algo vai acontecer no futuro
Uma Promise é um objeto que representa uma operação futura. Ela existe em 3 estados — e só passa por cada um uma vez, em sequência.
// Criando uma Promise const minhaPromise = new Promise((resolve, reject) => { const deuCerto = true if (deuCerto) { resolve("🎉 Dados chegaram!") // Fulfilled } else { reject("💥 Algo deu errado") // Rejected } }) // Consumindo com .then() / .catch() minhaPromise .then(resultado => console.log(resultado)) .catch(erro => console.log(erro)) .finally(() => console.log("Sempre executa"))
Você pode encadear
.then()
para processar dados em etapas, como um pipeline. Cada
.then() recebe o
resultado do anterior.
A forma moderna e legível de escrever código assíncrono
async/await
é açúcar sintático sobre Promises — faz código assíncrono
parecer e se comportar como síncrono, mas sem
travar. É muito mais legível que cadeias de
.then().
// Comparação: .then() vs async/await // ❌ Com .then() — difícil de ler buscarUsuario() .then(u => buscarPosts(u.id)) .then(posts => renderizar(posts)) .catch(err => tratarErro(err)) // ✅ Com async/await — limpo! async function carregarPagina() { try { const usuario = await buscarUsuario() const posts = await buscarPosts(usuario.id) renderizar(posts) } catch (erro) { tratarErro(erro) } } // await só funciona DENTRO de funções async // try/catch captura erros das Promises
// Paralelo com Promise.all — muito mais rápido! async function carregarTudo() { // ❌ Em série: 3s + 2s + 1s = 6 segundos const a = await tarefa1() // espera 3s const b = await tarefa2() // espera mais 2s // ✅ Em paralelo: max(3s, 2s, 1s) = 3 segundos! const [x, y, z] = await Promise.all([ tarefa1(), tarefa2(), tarefa3() ]) }
Simule chamadas à API e veja async/await em ação
async function buscarDados() { try { const res = await fetch("/api/...") const dados = await res.json() return dados } catch (erro) { console.log("Erro:", erro) } } // ← Escolha um endpoint e clique em Executar
A ponte entre JavaScript e a página HTML
O DOM é a representação da página HTML em forma de árvore de objetos que o JavaScript pode ler e modificar. Quando o browser carrega uma página, ele cria esse modelo — e o JS pode acessar cada elemento como um objeto.
Encontrar elementos na página com querySelector
Antes de modificar qualquer coisa, você precisa encontrar o elemento. O JS usa os mesmos seletores CSS que você já conhece.
// Por ID — retorna 1 elemento const titulo = document.getElementById("titulo") // Por seletor CSS — retorna o 1º encontrado const btn = document.querySelector(".btn-primario") const h1 = document.querySelector("h1") const input = document.querySelector("#email") // Por seletor — retorna TODOS (NodeList) const itens = document.querySelectorAll("li") const btns = document.querySelectorAll(".btn") // Percorrer todos itens.forEach(item => console.log(item.textContent))
querySelector — ele
aceita qualquer seletor CSS e é mais flexível que os métodos
antigos.
Este é um parágrafo com texto de exemplo.
🖼️ #logoMudar texto, estilos, classes e criar elementos novos
const el = document.querySelector("#caixa") // Mudar conteúdo el.textContent = "Novo texto!" // só texto el.innerHTML = "<b>Negrito!</b>" // HTML dentro // Mudar estilos CSS el.style.color = "red" el.style.fontSize = "24px" el.style.display = "none" // esconde // Gerenciar classes (preferível!) el.classList.add("ativo") el.classList.remove("ativo") el.classList.toggle("ativo") // liga/desliga // Criar e inserir elementos const novo = document.createElement("p") novo.textContent = "Sou novo!" document.body.appendChild(novo)
Reagir às ações do usuário — cliques, teclas, scroll…
Eventos permitem que o JS reaja ao que o usuário faz. Você "escuta" um evento num elemento e define o que acontece quando ele ocorre.
const btn = document.querySelector("#meu-btn") // Ouvir um evento btn.addEventListener("click", (event) => { console.log("Clicado!", event) }) // Eventos comuns: // "click" → clique do mouse // "input" → usuário digita // "submit" → formulário enviado // "keydown" → tecla pressionada // "mouseover" → mouse passou por cima // "DOMContentLoaded" → página carregou // Objeto event tem info útil: input.addEventListener("input", (e) => { console.log(e.target.value) // o que foi digitado })
Um sistema de controle de versão distribuído
Git é uma ferramenta que registra cada mudança no seu código ao longo do tempo. Você pode voltar a qualquer momento anterior, trabalhar em paralelo com outras pessoas e nunca perder trabalho.
Os 10 comandos que você usa 90% do tempo
git init
Inicializa um repositório
git clone
Clona repositório remoto
git status
Ver estado atual
git add
Preparar arquivos
git commit
Salvar checkpoint
git push
Enviar pro servidor
git pull
Baixar atualizações
git branch
Criar/listar branches
git checkout
Trocar de branch
git merge
Juntar branches
git log
Ver histórico
git diff
Ver mudanças
Linhas paralelas de desenvolvimento
Uma branch é uma linha paralela de desenvolvimento. Você cria uma cópia independente do código para desenvolver uma feature ou corrigir um bug — sem afetar o código principal.
# Criar e entrar na branch git checkout -b feature/login # Ver todas as branches git branch # * feature/login ← branch atual # main # Trabalhar normalmente... git add . git commit -m "feat: adicionar formulário de login" # Voltar para main e fazer merge git checkout main git merge feature/login # Apagar branch após merge git branch -d feature/login
Pratique o fluxo de trabalho completo
Receitas de passo a passo para resolver problemas
Um algoritmo é uma sequência finita de instruções para resolver um problema. Todo código que você escreve é um algoritmo — mas alguns são mais eficientes que outros.
Linear vs. Binária — veja a diferença
Verifica cada elemento um a um até achar o alvo. Simples mas lento para listas grandes.
Funciona em listas ordenadas. Vai direto ao meio, descarta metade dos elementos a cada passo. Muito mais rápido.
// Linear Search — O(n) function linearSearch(arr, target) { for (let i = 0; i < arr.length; i++) { if (arr[i] === target) return i } return -1 } // Binary Search — O(log n) — precisa estar ordenado! function binarySearch(arr, target) { let low = 0, high = arr.length - 1 while (low <= high) { const mid = Math.floor((low + high) / 2) if (arr[mid] === target) return mid if (arr[mid] < target) low = mid + 1 else high = mid - 1 } return -1 }
Bubble Sort vs. Merge Sort — visualize ao vivo
Compara pares adjacentes e os troca se estiverem fora de ordem. Fácil de entender, mas lento para listas grandes.
// Bubble Sort — O(n²) — simples mas lento function bubbleSort(arr) { for (let i = 0; i < arr.length; i++) { for (let j = 0; j < arr.length-i-1; j++) { if (arr[j] > arr[j+1]) { [arr[j], arr[j+1]] = [arr[j+1], arr[j]] // swap } } } return arr } // Merge Sort — O(n log n) — eficiente! function mergeSort(arr) { if (arr.length <= 1) return arr const mid = Math.floor(arr.length / 2) const L = mergeSort(arr.slice(0, mid)) const R = mergeSort(arr.slice(mid)) return merge(L, R) }
Como medir a eficiência de um algoritmo
Big O descreve como o tempo de execução cresce conforme o tamanho da entrada aumenta. É a linguagem universal para comparar algoritmos.
Uma função que chama a si mesma
Recursividade é quando uma função chama a si mesma para resolver versões menores do mesmo problema, até chegar a um caso simples que pode ser resolvido diretamente.
// ✅ Correto — tem base case! function fatorial(n) { if (n <= 1) return 1 // ← BASE CASE return n * fatorial(n - 1) // ← RECURSIVE CASE } fatorial(5) // = 5 * fatorial(4) // = 5 * 4 * fatorial(3) // = 5 * 4 * 3 * fatorial(2) // = 5 * 4 * 3 * 2 * fatorial(1) // = 5 * 4 * 3 * 2 * 1 = 120 ✅ // ❌ Errado — sem base case = stack overflow! function infinita(n) { return infinita(n - 1) // nunca para! }
Visualize como as chamadas se empilham e desempilham
A call stack é uma pilha de chamadas de função. Cada vez que uma função é chamada, ela entra na pilha. Quando retorna, sai. Em recursão, muitas chamadas se acumulam antes de começar a retornar.
Visualize a explosão de chamadas recursivas
fib(n) = fib(n-1) + fib(n-2). Parece simples, mas cada chamada gera duas novas chamadas, criando uma árvore exponencial — é O(2ⁿ)!
// ❌ Ingênuo — O(2ⁿ) — muito lento! function fib(n) { if (n <= 1) return n return fib(n-1) + fib(n-2) } // ✅ Com memoização — O(n) — muito mais rápido! function fibMemo(n, memo = {}) { if (n in memo) return memo[n] if (n <= 1) return n memo[n] = fibMemo(n-1, memo) + fibMemo(n-2, memo) return memo[n] }
Execute funções recursivas e veja o resultado passo a passo
Soluções reutilizáveis para problemas comuns de design de software
Design Patterns são soluções testadas e documentadas para problemas que aparecem repetidamente no desenvolvimento de software. Você não inventa a solução — usa uma que já foi validada por milhares de devs.
O livro "Design Patterns: Elements of Reusable Object-Oriented Software" (1994), escrito pelos Gang of Four (GoF), documentou 23 padrões fundamentais que ainda são usados hoje.
Os mais importantes — clique para ver detalhes
O padrão por trás de eventos, React state e muito mais
Um Subject (publicador) mantém uma lista de Observers (assinantes). Quando algo muda no Subject, ele notifica todos automaticamente.
Crie objetos sem se preocupar com a implementação
Em vez de chamar
new Classe()
diretamente, você pede para uma
fábrica criar o objeto certo baseado em
parâmetros. Facilita extensão sem quebrar o código existente.
class NotificationFactory { static create(type, msg) { const types = { email: EmailNotification, sms: SMSNotification, push: PushNotification, } const Cls = types[type] if (!Cls) throw new Error(`Tipo desconhecido: ${type}`) return new Cls(msg) } } // Uso — não precisa saber COMO é criado const n = NotificationFactory.create("email", "Olá!") n.send() // → Enviando email: "Olá!"
A linguagem comum entre sistemas diferentes
Uma API é um contrato entre dois sistemas — define como eles podem se comunicar. Uma API REST usa o protocolo HTTP para trocar dados (geralmente em JSON) entre cliente e servidor.
A linguagem da web
Cada operação tem um método semântico — o verbo que descreve o que você quer fazer com o recurso.
Todo response tem um código numérico indicando o resultado. Clique para ver o significado.
O formato padrão para troca de dados na web
JSON é um formato de texto para representar dados estruturados. Parece muito com objetos JS, mas tem regras estritas: chaves sempre entre aspas duplas, sem comentários, sem vírgula no último item.
{
"id": 1,
"nome": "Ana Silva",
"ativo": true,
"score": 9.5,
"tags": ["dev", "frontend"],
"endereco": {
"cidade": "São Paulo",
"uf": "SP"
},
"foto": null
}
// JSON → Objeto JS const jsonStr = '{"nome":"Ana","idade":28}' const obj = JSON.parse(jsonStr) console.log(obj.nome) // → "Ana" // Objeto JS → JSON const dados = { nome: "Bruno", ativo: true } const str = JSON.stringify(dados) // → '{"nome":"Bruno","ativo":true}' // Pretty print (indentação) JSON.stringify(dados, null, 2)
Faça requests para uma API fake e veja as respostas
// Selecione método e endpoint acima ↑ const res = await fetch("...") const dados = await res.json()
A interface que separa devs de usuários comuns
Quando você usa um computador, normalmente clica em ícones, arrasta janelas e interage com interfaces gráficas (GUI). O terminal é o outro caminho: você digita comandos de texto e o sistema operacional executa exatamente o que você pediu. Sem menus, sem cliques — só texto puro e poder absoluto.
Terminal é a janela (o programa) onde você digita. Shell é o interpretador que entende seus comandos (exemplos: Bash, Zsh, PowerShell). Console é o nome histórico que veio dos primeiros computadores físicos. No dia a dia, usamos esses termos como sinônimos — e tá tudo bem.
# O comando mais clássico de todos: echo "Hello, World!" # → Hello, World! # Ver o diretório atual: pwd # → /home/artclass # Listar arquivos da pasta: ls # → Documents Downloads projeto index.html
Todo comando no terminal segue este padrão:
Os 10 comandos que você vai usar todos os dias
Esses são os comandos mais usados no dia a dia de qualquer desenvolvedor. Com eles, você navega pelo sistema, cria e remove arquivos, e encontra o que precisa. Clique em cada card abaixo para ver a sintaxe e um exemplo prático.
ls
Listar arquivos e pastas
cd
Navegar entre diretórios
mkdir
Criar uma nova pasta
touch
Criar um novo arquivo
rm
Remover arquivo ou pasta
cp
Copiar arquivo ou pasta
mv
Mover ou renomear
cat
Ver conteúdo de arquivo
grep
Buscar texto em arquivos
echo
Imprimir texto no terminal
clear).
# Um workflow real de dev — criando um projeto do zero: mkdir meu-projeto # cria a pasta cd meu-projeto # entra nela touch index.html # cria arquivo HTML touch style.css # cria arquivo CSS mkdir src # cria pasta para JS touch src/app.js # cria o arquivo principal ls -la # verifica tudo criado # → index.html style.css src/
O pipe | conecta a saída de um comando à entrada de outro. O > redireciona saída para um arquivo. Isso permite criar cadeias poderosas de comandos.
# Encontrar todos os arquivos .js e contar quantos são: find . -name "*.js" | wc -l # → 23 # Ver os processos que mais usam memória: ps aux | sort -k4 -rn | head -5 # Salvar listagem em um arquivo: ls -la > lista.txt # Adicionar ao final (sem sobrescrever): echo "nova linha" >> lista.txt
Como o Linux organiza tudo — e quem pode acessar o quê
No Linux, tudo começa na raiz /. Diferente do Windows (C:\, D:\), o Linux tem uma única árvore de diretórios. Cada pasta tem um propósito específico. Conheça as mais importantes:
Absoluto começa com
/ — é o endereço completo
desde a raiz. Ex: /home/artclass/projetos
Relativo parte do
diretório atual. Ex: ../projetos (volta uma pasta,
depois entra em projetos).
~ é um atalho para
/home/seu-usuario. Ex: cd ~/projetos
No Linux, cada arquivo tem 3 grupos de permissão: dono, grupo e outros. Cada grupo pode ter permissão de leitura (r=4), escrita (w=2) e execução (x=1). Clique nos botões abaixo para montar o chmod!
chmod 755 script.sh — dá
permissão total ao dono e leitura+execução para o resto.
chmod 644 arquivo.txt — dono
lê e escreve, resto só lê.
# Ver permissões detalhadas: ls -la # → -rwxr-xr-x 1 artclass staff 2048 Mar 3 app.js # │├─┤├─┤├─┤ # │ u g o ← dono / grupo / outros # └ tipo (- = arquivo, d = diretório) # Tornar um script executável: chmod +x deploy.sh # Mudar dono do arquivo: chown artclass:devs projeto/
Digite comandos reais e veja o resultado ao vivo
ls,
cd pasta,
mkdir projeto,
touch arquivo.txt e muito
mais. Digite help para ver
todos os comandos disponíveis.
Complete os passos abaixo usando o terminal acima. A checklist vai atualizar automaticamente conforme você avança.
projeto
com
mkdir projeto
cd projeto
index.html
com
touch index.html
ls
se o arquivo foi criado
echo "Hello" > index.html
cat index.html
A linguagem universal dos bancos de dados
SQL é a linguagem usada para conversar com bancos de dados. Com ela você pode perguntar "quais usuários têm mais de 18 anos?" ou "adicione este novo produto ao catálogo" — e o banco responde ou executa na hora.
Os dados são organizados em tabelas — como planilhas do Excel. Cada tabela tem colunas (campos) e linhas (registros).
-- Minha primeira consulta SQL! -- SELECT = "me mostre" FROM = "da tabela" SELECT * FROM produtos; -- → Retorna TODAS as colunas e TODAS as linhas SELECT nome, preco FROM produtos; -- → Retorna só nome e preço de cada produto SELECT nome, preco FROM produtos WHERE preco > 300; -- → Só produtos acima de R$300 SELECT * FROM produtos ORDER BY preco DESC; -- → Todos, ordenados do mais caro ao mais barato
Query = consulta/pergunta ao banco · Schema = estrutura das tabelas · PK (Primary Key) = identificador único de cada linha · FK (Foreign Key) = referência a outra tabela
Create, Read, Update, Delete — tudo que você faz com dados
CRUD é a sigla para as 4 operações fundamentais que todo sistema faz com dados. Qualquer app que você usa — Instagram, Spotify, iFood — por baixo está fazendo CRUD o tempo todo.
Adicionar novos registros à tabela
INSERT INTO
para criar linhas novas. Você especifica a tabela, as colunas e
os valores.
Consultar e buscar dados
SELECT
lê dados com filtros (WHERE), ordenação (ORDER BY) e agrupamento (GROUP BY).
Modificar dados existentes
UPDATE ... SET
altera valores. Sempre use WHERE — sem filtro,
atualiza TODAS as linhas!
Remover registros
DELETE FROM ... WHERE
remove linhas. Cuidado: sem WHERE, apaga TUDO
da tabela!
-- ➕ CREATE — adicionar produto INSERT INTO produtos (nome, preco, categoria, estoque) VALUES ('Headset Gamer', 199, 'Periféricos', 20); -- 🔍 READ — buscar produtos caros SELECT nome, preco FROM produtos WHERE preco > 500 ORDER BY preco DESC; -- ✏️ UPDATE — dar desconto de 10% UPDATE produtos SET preco = preco * 0.9 WHERE categoria = 'Eletrônicos'; -- 🗑️ DELETE — remover sem estoque DELETE FROM produtos WHERE estoque = 0;
UPDATE ou
DELETE sem
WHERE! Sem filtro, o
comando afeta TODAS as linhas da tabela. Sempre
teste primeiro com
SELECT para ver o que
seria afetado.
-- Funções que calculam sobre grupos de dados SELECT COUNT(*) FROM produtos; -- → Quantos produtos existem? (ex: 6) SELECT SUM(estoque) FROM produtos; -- → Soma total do estoque (ex: 181) SELECT AVG(preco) FROM produtos; -- → Preço médio (ex: 1164.67) SELECT MIN(preco), MAX(preco) FROM produtos; -- → Mais barato e mais caro SELECT categoria, COUNT(*) as total FROM produtos GROUP BY categoria; -- → Quantos produtos por categoria
Conectando tabelas para obter dados completos
Na vida real, dados estão espalhados em várias tabelas. Uma loja tem tabela de "produtos", outra de "pedidos", outra de "clientes". O JOIN conecta essas tabelas usando um campo em comum.
Primary Key (PK) — identificador único de cada
linha (geralmente
id). Nenhuma linha pode ter o mesmo PK.
Foreign Key (FK) — campo que
referencia o PK de outra tabela. É o "ponteiro"
que cria a relação.
-- INNER JOIN — retorna só onde há correspondência -- "Me mostre o nome do cliente e seus pedidos" SELECT clientes.nome, pedidos.produto, pedidos.valor FROM clientes INNER JOIN pedidos ON clientes.id = pedidos.cliente_id; -- Resultado: -- Ana | Notebook | 3500 -- Ana | Mouse | 189 -- Bruno | Monitor | 2200 -- (Carlos não aparece — não tem pedidos!)
Retorna só o que conecta nas DUAS tabelas
Tudo da esquerda + correspondências da direita
NULL). Útil para encontrar "clientes que nunca compraram".
Tudo da direita + correspondências da esquerda
Tudo de ambas as tabelas
-- LEFT JOIN — todos da esquerda, mesmo sem match SELECT clientes.nome, pedidos.produto FROM clientes LEFT JOIN pedidos ON clientes.id = pedidos.cliente_id; -- Resultado: -- Ana | Notebook -- Ana | Mouse -- Bruno | Monitor -- Carlos | NULL ← aparece, mas sem pedido! -- Encontrar quem NUNCA comprou: SELECT clientes.nome FROM clientes LEFT JOIN pedidos ON clientes.id = pedidos.cliente_id WHERE pedidos.id IS NULL; -- → Carlos
Escreva queries e veja os resultados ao vivo!
SELECT nome FROM produtos WHERE estoque > 30 ORDER BY nome. Ou insira novos itens e veja a tabela crescer em tempo real!