SQL basics: what a query actually is, SELECT, WHERE, JOIN
Every database-driven app you've used runs on this. Once SELECT / WHERE / JOIN click, the rest of SQL is just vocabulary on top of the same three ideas.
SQL (Structured Query Language) is the language you use to ask a relational database questions and to change the data inside it. It's not a programming language in the sense of Python or JavaScript — you don't write loops or functions in plain SQL. You describe what result you want, and the database figures out how to get it.
A query is just one SQL statement that asks for data — most commonly a SELECT statement. When someone says "run this query," they mean "ask the database this question and show me the answer."
Why this matters even if you never write raw SQL: ORMs (Prisma, Django's ORM, ActiveRecord), Supabase's client library, and most backend frameworks all translate your code into SQL behind the scenes. Reading a slow query log or debugging "why did this return the wrong rows" requires knowing what SQL actually got generated.
2. Tables, rows, columns — the mental model
A relational database stores data in tables — think spreadsheets with strict rules. Each table has a fixed set of columns (name, type, e.g. email as text, created_at as a timestamp), and each row is one record.
id
name
email
plan
1
Alice
alice@example.com
pro
2
Bruno
bruno@example.com
free
3
Carla
carla@example.com
pro
This is a users table. Every row follows the same shape — that consistency is what lets SQL ask precise questions across thousands or millions of rows in milliseconds.
3. SELECT: picking what you want to see
SELECT is how you choose which columns to return. The * wildcard means "all columns."
SELECT * FROM users;
SELECT name, email FROM users;
The second query only returns the name and email columns for every row — narrower results are faster and easier to work with, which matters once a table has millions of rows instead of three.
4. WHERE: filtering rows
WHERE adds a condition — only rows that match come back.
SELECT name, email FROM users
WHERE plan = 'pro';
This returns only Alice and Carla. You can combine conditions with AND / OR, and use operators like >, <, LIKE (pattern matching on text), and IN (matches any value in a list):
SELECT * FROM users
WHERE plan = 'pro' AND created_at > '2026-01-01';
Common trap:WHERE filters rows, not columns — that's what SELECT is for. Mixing the two up ("why isn't this hiding the column") is one of the most common early confusions.
5. JOIN: combining tables
Real apps split data across multiple tables instead of one giant one — a users table and a separate orders table, linked by a shared id (a foreign key, e.g. orders.user_id pointing at users.id). JOIN is how you pull related rows from both tables into one result.
Join type
What it returns
INNER JOIN
Only rows that have a match in both tables. The default and most common join.
LEFT JOIN
All rows from the left table, plus matches from the right table — unmatched rows show NULL instead of being dropped. Useful for "show me all users, even ones with zero orders."
RIGHT JOIN
The mirror of LEFT JOIN — rarely used in practice, since you can usually just swap table order and use LEFT JOIN instead.
SELECT users.name, orders.total
FROM users
JOIN orders ON users.id = orders.user_id
WHERE orders.total > 100;
This reads as: "give me the user's name and order total, for every order over 100, matching orders to their user via the shared id." That last part — ON users.id = orders.user_id — is the actual join condition; without it, the database would try to pair every row in one table with every row in the other, producing a nonsensical, huge result (a "cartesian product").
6. Why SQL, why now
SQL has been the standard for relational databases since the 1970s, and it hasn't been displaced because the underlying problem — structured data with relationships between records — hasn't gone away. It exists because:
It's declarative. You describe the result, the database's query planner decides the fastest way to fetch it (which index to use, which order to join tables). You don't hand-write the retrieval algorithm.
It enforces structure. Columns have types, tables can have constraints (a column can't be empty, an email must be unique) — this catches bad data before it gets stored, unlike loosely-structured alternatives.
It's a portable skill. Postgres, MySQL, SQLite, SQL Server all speak dialects of the same core language — learning SELECT/WHERE/JOIN once transfers almost entirely across engines.
The main alternative — NoSQL / document databases (MongoDB and similar) — trades relational structure for flexibility and, in some cases, easier horizontal scaling. That comparison is coming up as its own lesson later this week; for now, the point is that SQL is the default you reach for unless you have a specific reason not to.
7. A practical example, start to finish
Say you're building a simple blog and want: the title of every published post, along with the name of its author, newest first.
SELECT posts.title, authors.name
FROM posts
JOIN authors ON posts.author_id = authors.id
WHERE posts.status = 'published'
ORDER BY posts.created_at DESC;
Reading it top to bottom in plain language: pick the title and author name, from the posts table joined with authors matching on author_id, but only published posts, sorted by newest first. That's the whole shape of most real-world queries — SELECT the columns, JOIN the related tables, WHERE to filter, and usually an ORDER BY to sort. Everything else in SQL (grouping, aggregates like COUNT/SUM, subqueries) builds on top of this same skeleton.
Share:
Newsletter
Learn what AI is creating for you. Don't get lost.
Get notified when a new deep dive or lesson goes up. No spam, just new posts.
Lição 11 · Dados e backend
SQL básico: o que é uma query, SELECT, WHERE, JOIN
Todo app com banco de dados que você já usou roda em cima disso. Depois que SELECT / WHERE / JOIN fizerem sentido, o resto do SQL é só vocabulário em cima das mesmas três ideias.
SQL (Structured Query Language) é a linguagem usada pra fazer perguntas a um banco relacional e pra mudar os dados dentro dele. Não é uma linguagem de programação no sentido de Python ou JavaScript — você não escreve loops ou funções em SQL puro. Você descreve que resultado você quer, e o banco decide como buscar.
Uma query é só uma instrução SQL que pede dados — mais comumente uma instrução SELECT. Quando alguém diz "roda essa query", quer dizer "faz essa pergunta ao banco e me mostra a resposta".
Por que isso importa mesmo se você nunca escrever SQL puro: ORMs (Prisma, o ORM do Django, ActiveRecord), a biblioteca cliente do Supabase, e a maioria dos frameworks de backend traduzem seu código pra SQL por debaixo dos panos. Ler um log de query lenta ou debugar "por que isso retornou as linhas erradas" exige saber qual SQL foi de fato gerado.
2. Tabelas, linhas, colunas — o modelo mental
Um banco relacional guarda dados em tabelas — pense em planilhas com regras estritas. Cada tabela tem um conjunto fixo de colunas (nome, tipo, ex: email como texto, created_at como timestamp), e cada linha é um registro.
id
nome
email
plano
1
Alice
alice@example.com
pro
2
Bruno
bruno@example.com
free
3
Carla
carla@example.com
pro
Essa é uma tabela users. Toda linha segue o mesmo formato — essa consistência é o que permite ao SQL fazer perguntas precisas em milhares ou milhões de linhas em milissegundos.
3. SELECT: escolhendo o que você quer ver
SELECT é como você escolhe quais colunas retornar. O curinga * significa "todas as colunas".
SELECT * FROM users;
SELECT name, email FROM users;
A segunda query retorna só as colunas name e email de cada linha — resultados mais estreitos são mais rápidos e mais fáceis de trabalhar, o que importa quando uma tabela tem milhões de linhas em vez de três.
4. WHERE: filtrando linhas
WHERE adiciona uma condição — só as linhas que combinam voltam.
SELECT name, email FROM users
WHERE plan = 'pro';
Isso retorna só Alice e Carla. Você pode combinar condições com AND / OR, e usar operadores como >, <, LIKE (comparação de padrão em texto) e IN (compara com qualquer valor de uma lista):
SELECT * FROM users
WHERE plan = 'pro' AND created_at > '2026-01-01';
Confusão comum:WHERE filtra linhas, não colunas — isso é papel do SELECT. Misturar os dois ("por que isso não está escondendo a coluna") é uma das confusões iniciais mais comuns.
5. JOIN: combinando tabelas
Apps de verdade dividem dados em várias tabelas em vez de uma gigante só — uma tabela users e uma tabela separada orders, ligadas por um id compartilhado (uma foreign key, ex: orders.user_id apontando pra users.id). JOIN é como você puxa linhas relacionadas das duas tabelas num resultado só.
Tipo de join
O que retorna
INNER JOIN
Só linhas que têm correspondência nas duas tabelas. O join padrão e mais comum.
LEFT JOIN
Todas as linhas da tabela da esquerda, mais as correspondências da tabela da direita — linhas sem correspondência mostram NULL em vez de serem descartadas. Útil pra "me mostra todos os usuários, até os com zero pedidos".
RIGHT JOIN
O espelho do LEFT JOIN — raramente usado na prática, já que geralmente basta trocar a ordem das tabelas e usar LEFT JOIN.
SELECT users.name, orders.total
FROM users
JOIN orders ON users.id = orders.user_id
WHERE orders.total > 100;
Isso lê como: "me dá o nome do usuário e o total do pedido, pra todo pedido acima de 100, combinando pedidos com seus usuários via o id compartilhado". Essa última parte — ON users.id = orders.user_id — é a condição de join de fato; sem ela, o banco tentaria combinar cada linha de uma tabela com cada linha da outra, produzindo um resultado gigante e sem sentido (um "produto cartesiano").
6. Por que SQL, por que agora
SQL é o padrão pra bancos relacionais desde os anos 1970, e não foi substituído porque o problema de base — dados estruturados com relações entre registros — não desapareceu. Ele existe porque:
É declarativo. Você descreve o resultado, o query planner do banco decide o jeito mais rápido de buscar (qual índice usar, em que ordem juntar as tabelas). Você não escreve o algoritmo de busca na mão.
Impõe estrutura. Colunas têm tipos, tabelas podem ter restrições (uma coluna não pode ficar vazia, um email precisa ser único) — isso pega dado ruim antes de ser salvo, diferente de alternativas com estrutura mais solta.
É uma habilidade portátil. Postgres, MySQL, SQLite, SQL Server todos falam dialetos da mesma linguagem base — aprender SELECT/WHERE/JOIN uma vez transfere quase inteiramente entre motores.
A principal alternativa — bancos NoSQL / de documentos (MongoDB e similares) — troca estrutura relacional por flexibilidade e, em alguns casos, escala horizontal mais fácil. Essa comparação vem como lição própria ainda essa semana; por ora, o ponto é que SQL é o padrão que você usa a não ser que tenha um motivo específico pra não usar.
7. Um exemplo prático, do início ao fim
Digamos que você está construindo um blog simples e quer: o título de todo post publicado, junto com o nome do autor, do mais novo pro mais antigo.
SELECT posts.title, authors.name
FROM posts
JOIN authors ON posts.author_id = authors.id
WHERE posts.status = 'published'
ORDER BY posts.created_at DESC;
Lendo de cima a baixo em linguagem simples: pega o título e o nome do autor, da tabela posts juntada com authors combinando por author_id, mas só posts publicados, ordenado pelo mais novo primeiro. Esse é o formato de praticamente toda query do mundo real — SELECT as colunas, JOIN as tabelas relacionadas, WHERE pra filtrar, e geralmente um ORDER BY pra ordenar. Todo o resto do SQL (agrupamento, agregados como COUNT/SUM, subqueries) constrói em cima desse mesmo esqueleto.
Compartilhar:
Newsletter
Saiba o que a IA está criando pra você. Não fique por fora.
Seja avisado quando eu postar uma imersão ou lição nova. Sem spam, só posts novos.