← Back to Cooldecode
Lesson 11 · Data & backend

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.

1. What SQL is, and what a "query" means

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.

idnameemailplan
1Alicealice@example.compro
2Brunobruno@example.comfree
3Carlacarla@example.compro

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 typeWhat it returns
INNER JOINOnly rows that have a match in both tables. The default and most common join.
LEFT JOINAll 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 JOINThe 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:

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.

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.

1. O que é SQL, e o que significa "query"

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.

idnomeemailplano
1Alicealice@example.compro
2Brunobruno@example.comfree
3Carlacarla@example.compro

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 joinO que retorna
INNER JOINSó linhas que têm correspondência nas duas tabelas. O join padrão e mais comum.
LEFT JOINTodas 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 JOINO 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:

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.

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.