Modelo relacional de bases de dados
Apresentação
UC00602 (25h · 2,25 pts) introduz o modelo relacional. Aprende-se a modelar dados a partir de requisitos do mundo real, desenhar diagramas entidade-relação, aplicar normalização até à 3FN e definir chaves e restrições.
Do real ao relacional
Programar sem um bom modelo de dados é construir uma casa sem fundações. A maioria dos bugs e da dor em projectos vem de modelos mal pensados no início.
Conceitos
- Entidade — "coisa" do mundo real que queremos representar: Aluno, Curso, Encomenda.
- Atributo — propriedade da entidade: nome do aluno, preço do produto.
- Instância — ocorrência concreta: o aluno "João Silva" com id 42.
- Relação — associação entre entidades: Aluno inscreve-se em Curso.
- Cardinalidade — quantas instâncias se relacionam: um, muitos.
- Chave primária (PK) — coluna(s) que identifica unicamente cada linha.
- Chave estrangeira (FK) — coluna que aponta para a PK de outra tabela.
Cardinalidades
1:1 — um-para-um Cada instância de A liga-se a no máximo uma de B, e vice-versa. Exemplo: Pessoa ↔ Cartão Cidadão.
1:N — um-para-muitos Cada A pode ligar-se a várias B; cada B liga-se a apenas um A. Exemplo: Cliente → Encomendas. Implementa-se com FK na "muitos" (Encomenda.cliente_id).
N:M — muitos-para-muitos A pode ligar-se a várias B e vice-versa. Exemplo: Aluno ↔ Curso. Resolve-se sempre com tabela de associação (Inscricao) com FK para os dois lados.
Diagramas ER
Notações
Chen — académica. Rectângulos para entidades, losangos para relações, óvalos para atributos. Verbosa mas didática.
Crow's Foot / Information Engineering — a mais usada em 2026. Rectângulos com lista de colunas; linhas entre rectângulos com "pés de galinha" (representam o lado "muitos"). Compacta.
UML — class diagrams adaptados. Comum em ambientes onde já se usa UML para outras coisas.
Ferramentas
- dbdiagram.io — gratuito, DSL simples, exporta SQL.
- drawSQL.app — interface visual moderna.
- MySQL Workbench — editor visual + sincronização com BD real.
- DBeaver — cliente BD que também desenha modelos.
- DataGrip (pago, JetBrains) — excelente para projectos sérios.
- Lucidchart, diagrams.net — uso geral, suporta ER.
Exemplo dbdiagram.io
Table Cliente {
id int [pk, increment]
nome varchar
email varchar [unique]
}
Table Encomenda {
id int [pk, increment]
data datetime
cliente_id int [ref: > Cliente.id]
}
A sintaxe [ref: > Cliente.id] cria FK de Encomenda.cliente_id para Cliente.id em relação N:1.
Normalização
Conjunto de regras que reduzem redundância e anomalias de actualização. Foi descrita por Codd nos anos 70.
1FN · Primeira Forma Normal
Cada célula contém um valor atómico. Não há listas, conjuntos ou estruturas dentro de uma célula.
Errado: | id | aluno | telefones | |---|---|---| | 1 | João | 919..., 933... |
Correcto: tabela Telefone separada com FK aluno_id.
2FN · Segunda Forma Normal
1FN + cada atributo não-chave depende da chave inteira (relevante só com chaves compostas).
Errado:
| encomenda_id | produto_id | preco_unit | nome_produto |
|---|---|---|---|
| 1 | 5 | 12.50 | Caneta azul |
nome_produto depende só de produto_id, não da chave composta. Mover nome_produto para a tabela Produto.
3FN · Terceira Forma Normal
2FN + sem dependências transitivas (atributos não-chave que dependem de outros atributos não-chave).
Errado:
| aluno_id | nome | cod_postal | cidade |
|---|---|---|---|
| 42 | João | 1000-001 | Lisboa |
cidade depende de cod_postal, não directamente do aluno. Mover para tabela CodigoPostal.
BCNF, 4FN, 5FN
Formas mais avançadas. Na maioria dos projectos práticos, parar na 3FN é suficiente.
Desnormalização
Em sistemas de leitura intensiva (relatórios, dashboards, BI), desnormalizar pode melhorar performance — duplica-se dado para evitar JOINs em queries massivas. Exemplo: relatório mensal de vendas com nome do cliente já replicado no extracto.
Regra: normalizar primeiro; desnormalizar com medição e motivo claro.
Restrições
Tipos
| Restrição | Exemplo SQL |
|---|---|
| PRIMARY KEY | id INT PRIMARY KEY |
| FOREIGN KEY | cliente_id INT REFERENCES Cliente(id) |
| UNIQUE | email VARCHAR(150) UNIQUE |
| NOT NULL | nome VARCHAR(80) NOT NULL |
| CHECK | idade INT CHECK (idade >= 0) |
| DEFAULT | criado_em DATETIME DEFAULT CURRENT_TIMESTAMP |
Acções referenciais
Quando se altera ou apaga uma linha "pai" que tem filhos referenciando-a:
- CASCADE — propaga a operação aos filhos. Exemplo: apagar Encomenda apaga EncomendaItem em cascata.
- SET NULL — coloca a FK a NULL nos filhos. Exemplo: apagar Departamento → FuncionarioId.depto_id = NULL.
- RESTRICT ou NO ACTION — impede a operação se houver filhos.
- SET DEFAULT — coloca valor por defeito.
CREATE TABLE EncomendaItem (
encomenda_id INT,
produto_id INT,
qty INT,
PRIMARY KEY (encomenda_id, produto_id),
FOREIGN KEY (encomenda_id) REFERENCES Encomenda(id) ON DELETE CASCADE,
FOREIGN KEY (produto_id) REFERENCES Produto(id) ON DELETE RESTRICT
);
Tipos de dados
Numéricos
INT/BIGINT— inteiros.DECIMAL(p,s)/NUMERIC(p,s)— preço, valores exactos.FLOAT/DOUBLE— aproximados (não usar para dinheiro).
Texto
CHAR(n)— comprimento fixo.VARCHAR(n)— comprimento variável até n.TEXT— texto longo sem limite definido.
Data e tempo
DATE— só data (2026-05-19).TIME— só hora.DATETIME/TIMESTAMP— data e hora.
TIMESTAMP em MySQL tem tratamento de timezone automático e limite até 2038.
Outros
BOOLEAN— true/false (mapeado paraTINYINT(1)em MySQL).JSON— objectos JSON (MySQL 5.7+, PostgreSQL).BLOB— binário (raramente usado; preferível guardar ficheiros em filesystem).
Caso prático · loja online
Requisitos
Pretende-se modelar uma loja online com: - Clientes com nome, email, morada. - Produtos com categoria, preço, stock. - Encomendas que incluem vários produtos em quantidades variáveis.
Modelo
CREATE TABLE Cliente (
id INT PRIMARY KEY AUTO_INCREMENT,
nome VARCHAR(150) NOT NULL,
email VARCHAR(150) UNIQUE NOT NULL,
telefone VARCHAR(20),
morada TEXT
);
CREATE TABLE Categoria (
id INT PRIMARY KEY AUTO_INCREMENT,
nome VARCHAR(80) UNIQUE NOT NULL
);
CREATE TABLE Produto (
id INT PRIMARY KEY AUTO_INCREMENT,
nome VARCHAR(150) NOT NULL,
descricao TEXT,
preco DECIMAL(10,2) NOT NULL CHECK (preco >= 0),
stock INT NOT NULL DEFAULT 0,
categoria_id INT,
FOREIGN KEY (categoria_id) REFERENCES Categoria(id)
);
CREATE TABLE Encomenda (
id INT PRIMARY KEY AUTO_INCREMENT,
data DATETIME DEFAULT CURRENT_TIMESTAMP,
cliente_id INT NOT NULL,
estado ENUM('pendente','pago','enviado','entregue','cancelado') DEFAULT 'pendente',
FOREIGN KEY (cliente_id) REFERENCES Cliente(id)
);
CREATE TABLE EncomendaItem (
encomenda_id INT,
produto_id INT,
qty INT NOT NULL CHECK (qty > 0),
preco_unit DECIMAL(10,2) NOT NULL,
PRIMARY KEY (encomenda_id, produto_id),
FOREIGN KEY (encomenda_id) REFERENCES Encomenda(id) ON DELETE CASCADE,
FOREIGN KEY (produto_id) REFERENCES Produto(id) ON DELETE RESTRICT
);
Notas:
- preco_unit em EncomendaItem regista o preço no momento da compra — não vai buscá-lo a Produto, porque o preço pode mudar depois.
- ENUM no estado da Encomenda restringe valores permitidos.
- CASCADE em itens (faz sentido apagar conjunto), RESTRICT em produto (não permitir apagar produto que está em encomendas históricas).
Apêndices
A · Cheatsheet PK / FK
-- PK simples
id INT PRIMARY KEY AUTO_INCREMENT
-- PK composta
PRIMARY KEY (col1, col2)
-- FK inline
cliente_id INT REFERENCES Cliente(id)
-- FK separada
FOREIGN KEY (cliente_id) REFERENCES Cliente(id)
ON DELETE CASCADE
ON UPDATE CASCADE
B · Glossário
Anomalia de actualização. Modelo mal normalizado obriga a actualizar várias linhas para mudar um dado, com risco de inconsistência. BCNF. Boyce-Codd Normal Form — versão mais estrita da 3FN. Cardinalidade. Quantos elementos participam numa relação. ER. Entity-Relationship. FK. Foreign Key — chave estrangeira. PK. Primary Key — chave primária.
C · Recursos
- dbdiagram.io — diagramas online gratuitos.
- Use The Index, Luke · use-the-index-luke.com — explica índices.
- SQLBolt · interactive tutorials.
- Livro: Database Design for Mere Mortals (Hernandez).