
Banco de Dados: Entendendo a Estrutura no SQL para Iniciantes
Bancos de dados relacionais sustentam redes sociais, e-commerce, ERPs e apps mobile. Antes de escrever sua primeira consulta SQL, entender como os dados são organizados (tabelas, chaves, tipos, relacionamentos) economiza horas de depuração e evita modelos frágeis. Este guia apresenta os blocos fundamentais que você usará diariamente.
O que é um banco de dados relacional?
Um SGBD relacional (PostgreSQL, MySQL, SQL Server, Oracle) organiza dados em tabelas (como planilhas):
- Linhas (registros/tuplas): cada ocorrência de dado.
- Colunas (campos/atributos): características do registro.
- Esquema (schema): definição formal da estrutura (tabelas, colunas, tipos, chaves).
- Instância: o conjunto de dados carregados naquele momento.
Exemplo de tabela clientes:
| id_cliente | nome | criado_em | |
|---|---|---|---|
| 1 | Ana Lima | [email protected] | 2025-09-10 |
| 2 | João Luz | [email protected] | 2025-09-12 |
Tipos de dados: a base da integridade
Escolher o tipo certo garante validação e performance:
- INTEGER/BIGINT – inteiros/identificadores.
- DECIMAL(p,s)/NUMERIC – valores financeiros (evite
FLOATp/ dinheiro). - VARCHAR(n)/TEXT – textos.
- DATE/TIME/TIMESTAMP – datas e horários.
- BOOLEAN – verdadeiro/falso.
- UUID – identificador global único (alguns SGBDs).
Boa prática: modele o tipo mais restrito que atende ao dado. Isso reduz armazenamento e previne entradas inválidas.
Chaves: primária, única e estrangeira
- Chave Primária (PK): identifica exclusivamente cada linha.
- Chave Única (UNIQUE): impede duplicidade em uma coluna/grupo (ex.:
email). - Chave Estrangeira (FK): cria o relacionamento entre tabelas garantindo referencial (ex.: cada pedido deve apontar para um cliente válido).
Modelando um mini-ERP (clientes ↔ pedidos ↔ itens)
CREATE TABLE clientes (
id_cliente BIGSERIAL PRIMARY KEY,
nome VARCHAR(120) NOT NULL,
email VARCHAR(180) UNIQUE NOT NULL,
criado_em TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE TABLE pedidos (
id_pedido BIGSERIAL PRIMARY KEY,
id_cliente BIGINT NOT NULL REFERENCES clientes(id_cliente)
ON UPDATE CASCADE ON DELETE RESTRICT,
status VARCHAR(20) NOT NULL CHECK (status IN ('novo','pago','enviado','cancelado')),
criado_em TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE TABLE itens_pedido (
id_item BIGSERIAL PRIMARY KEY,
id_pedido BIGINT NOT NULL REFERENCES pedidos(id_pedido) ON DELETE CASCADE,
sku VARCHAR(40) NOT NULL,
qtde INTEGER NOT NULL CHECK (qtde > 0),
preco_unit DECIMAL(12,2) NOT NULL CHECK (preco_unit >= 0)
);
ON DELETE CASCADEapaga itens quando o pedido é removido.ON DELETE RESTRICTimpede excluir um cliente com pedidos ativos.
Relacionamentos e cardinalidade
- 1:1 – um cliente ↔ uma carteira digital (raro).
- 1:N – um cliente → muitos pedidos (mais comum).
- N:M – pedidos ↔ cupons (tabela associativa
pedido_cupom).
CREATE TABLE pedido_cupom (
id_pedido BIGINT NOT NULL REFERENCES pedidos(id_pedido) ON DELETE CASCADE,
codigo VARCHAR(20) NOT NULL,
PRIMARY KEY (id_pedido, codigo)
);
Normalização (e quando desnormalizar)
Normalização reduz redundâncias e anomalias:
- 1NF: sem colunas multivalor; linhas atômicas.
- 2NF: colunas dependem da PK inteira (evita dependência parcial).
- 3NF: sem dependências transitivas (A → B → C).
Trade-off: às vezes desnormalizar (armazenar um subtotal, por ex.) melhora leitura. Faça isso conscientemente e com validações/rotinas para manter consistência.
Restrições e regras de negócio no banco
- NOT NULL – obriga valor.
- CHECK – valida domínio (
status IN (…),qtde > 0). - DEFAULT – valor padrão (
NOW()/CURRENT_DATE). - UNIQUE – evita duplicados.
- FOREIGN KEY – garante integridade referencial.
Centralizar regras no banco previne inconsistências caso existam múltiplos serviços/clientes acessando os dados.
Índices: quando e por quê
Índices aceleram filtros e junções (normalmente B-tree):
CREATE INDEX idx_pedidos_cliente ON pedidos(id_cliente);
CREATE INDEX idx_itens_pedido_pedido ON itens_pedido(id_pedido);
- Crie índices nas colunas mais filtradas (FKs, buscas por e-mail, datas).
- Evite excesso: cada índice custa escrita (INSERT/UPDATE/DELETE mais lentos).
Nulos, valores padrão e consistência
NULLsignifica “desconhecido/ausente” (não é zero nem vazio).- Comparações com
NULLusamIS NULL/IS NOT NULL. - Prefira
NOT NULL+DEFAULTquando há valor sensato.
Exemplo prático: do modelo à consulta
Pergunta: “Quais pedidos pagos do cliente com e-mail [email protected] nos últimos 30 dias?”
SELECT p.id_pedido, p.criado_em, SUM(i.qtde * i.preco_unit) AS total
FROM pedidos p
JOIN clientes c ON c.id_cliente = p.id_cliente
JOIN itens_pedido i ON i.id_pedido = p.id_pedido
WHERE c.email = '[email protected]'
AND p.status = 'pago'
AND p.criado_em >= NOW() - INTERVAL '30 days'
GROUP BY p.id_pedido, p.criado_em
ORDER BY p.criado_em DESC;
Boas práticas para quem está começando
- Nomeie de forma clara (snake_case, sem abreviações obscuras).
- Defina PKs desde o início (surrogate keys como
BIGSERIAL/UUID). - Planeje FKs – relações explícitas facilitam junções corretas.
- Adicione índices com parcimônia – meça antes/depois.
- Documente o esquema (diagramas ER, comentários no banco).
- Semeie dados de exemplo para testar cenários reais.
FAQ rápido
PK vs UNIQUE? PK identifica a linha e não aceita NULL; UNIQUE só restringe duplicidade e pode aceitar NULL (depende do SGBD).
Quando usar UUID? Em sistemas distribuídos/microserviços, quando você precisa gerar IDs sem coordenação central.
Dinheiro com FLOAT? Evite; use DECIMAL/NUMERIC para exatidão.
Este artigo foi inspirado no Capítulo 1 – “Understanding Database Structure” do livro SQL: QuickStart Guide – The Simplified Beginner’s Guide to SQL, de Walter Shields.