Ficha 02 · Normalização e restrições
- Aplicar 1FN/2FN/3FN
- Identificar redundâncias
- Definir restrições
- Escolher acções referenciais
Parte I · Formas normais
Exercício 1 · Identificar violação (15 pts)
Para cada caso, indica que forma normal é violada e como corrigir:
a)
| id | aluno | disciplinas |
|---|---|---|
| 1 | João | Matemática, Português, Inglês |
b)
| encomenda_id | produto_id | nome_produto | qty |
|---|---|---|---|
| 1 | 5 | Caneta | 10 |
c)
| aluno_id | cod_postal | localidade |
|---|---|---|
| 42 | 1000-001 | Lisboa |
a) Viola 1FN — disciplinas não é atómico. Corrigir: tabela Aluno_Disciplina (aluno_id, disciplina_id).
b) Viola 2FN — nome_produto depende só de produto_id, não da chave composta. Corrigir: mover nome_produto para tabela Produto.
c) Viola 3FN — localidade depende de cod_postal, não directamente do aluno (dependência transitiva). Corrigir: tabela CodigoPostal (cod_postal PK, localidade).
Exercício 2 · Normalizar (20 pts)
A tabela seguinte regista vendas:
| venda_id | data | cliente_id | nome_cliente | email_cliente | produto_id | nome_produto | preco | qty |
|---|---|---|---|---|---|---|---|---|
Identifica todos os problemas e propõe modelo normalizado:
Problemas:
1. nome_cliente, email_cliente dependem de cliente_id, não da venda → mover para tabela Cliente.
2. nome_produto, preco dependem de produto_id → mover para tabela Produto.
3. Uma venda só tem um produto na estrutura mostrada; provavelmente queremos uma venda com vários produtos → introduzir tabela VendaItem.
Modelo normalizado:
Cliente (id PK, nome, email)
Produto (id PK, nome, preco)
Venda (id PK, data, cliente_id FK)
VendaItem (
venda_id FK,
produto_id FK,
qty,
preco_unit, -- preço no momento da venda
PK (venda_id, produto_id)
)
Notar: preco_unit em VendaItem é necessário porque o preço actual do produto pode ter mudado desde a venda.
Exercício 3 · Quando desnormalizar (10 pts)
a) Em que situações pode fazer sentido desnormalizar uma BD bem normalizada?
b) Que cuidados ter quando se desnormaliza?
a) Situações: - Relatórios e BI com queries massivas que de outro modo exigiriam muitos JOINs. - Caches de valores calculados (totais, contagens). - Tabelas históricas/snapshot onde dados não devem mudar mesmo que o "pai" mude (ex.: nome do cliente numa fatura emitida).
b) Cuidados: - Justificar com medição (perfil de query antes/depois). - Documentar que dados estão duplicados e onde. - Triggers ou aplicação para manter cópias consistentes, ou aceitar consistência eventual. - Não desnormalizar prematuramente — é mais fácil desnormalizar depois do que renormalizar.
Parte II · Restrições
Exercício 4 · Definir restrições (20 pts)
Para a tabela Produto (id, nome, preco, stock, cat_id), escreve SQL com todas as restrições adequadas:
CREATE TABLE Produto (
id INT PRIMARY KEY AUTO_INCREMENT,
nome VARCHAR(150) NOT NULL UNIQUE,
preco DECIMAL(10,2) NOT NULL CHECK (preco >= 0),
stock INT NOT NULL DEFAULT 0 CHECK (stock >= 0),
cat_id INT,
FOREIGN KEY (cat_id) REFERENCES Categoria(id) ON DELETE SET NULL
);
Decisões:
- id PK auto-increment — chave artificial.
- nome UNIQUE — não queremos dois produtos com nome igual.
- preco DECIMAL — exacto, não usar FLOAT para dinheiro.
- CHECK (preco >= 0) e CHECK (stock >= 0) — evita valores absurdos.
- cat_id permite NULL (produto pode existir sem categoria); SET NULL se categoria for apagada.
Exercício 5 · Acções referenciais (15 pts)
Para cada relação, indica a acção ON DELETE mais apropriada e justifica:
a) Apagar Encomenda → o que fazer com EncomendaItem?
b) Apagar Cliente → o que fazer com Encomenda?
c) Apagar Categoria → o que fazer com Produto?
d) Apagar Autor → o que fazer com Livro_Autor (tabela de associação)?
a) CASCADE — itens da encomenda não fazem sentido sem a encomenda. Apaga-se tudo junto.
b) RESTRICT — não apagar cliente com encomendas históricas; a alternativa é "soft delete" (campo apagado = true). Apagar é raro em sistemas reais (RGPD pode obrigar mas com cuidados).
c) SET NULL — produto continua a existir mesmo sem categoria. Alternativa: criar categoria "Outros" como default.
d) CASCADE — entradas em Livro_Autor referindo autor apagado deixam de fazer sentido.
Parte III · Cenário
Exercício 6 · Sistema de gestão escolar (20 pts)
Pretende-se gerir disciplinas, alunos, professores, turmas e notas.
a) Identifica 5+ entidades.
b) Desenha pelo menos uma relação 1:N e uma N:M com tabela de associação.
c) Indica restrições importantes em pelo menos 3 colunas.
a) Entidades: - Aluno, Professor, Turma, Disciplina, Inscricao (associação Aluno-Disciplina), Aula (opcional).
b) Modelo:
CREATE TABLE Turma (
id INT PRIMARY KEY AUTO_INCREMENT,
codigo VARCHAR(10) UNIQUE NOT NULL,
ano_lectivo VARCHAR(9) NOT NULL -- ex.: "2026/2027"
);
CREATE TABLE Aluno (
id INT PRIMARY KEY AUTO_INCREMENT,
numero VARCHAR(10) UNIQUE NOT NULL,
nome VARCHAR(150) NOT NULL,
data_nascimento DATE,
turma_id INT,
FOREIGN KEY (turma_id) REFERENCES Turma(id) ON DELETE SET NULL
);
-- Aluno-Turma é 1:N: cada aluno numa turma, turma tem vários alunos.
CREATE TABLE Disciplina (
id INT PRIMARY KEY AUTO_INCREMENT,
codigo VARCHAR(20) UNIQUE NOT NULL,
nome VARCHAR(100) NOT NULL,
professor_id INT,
FOREIGN KEY (professor_id) REFERENCES Professor(id)
);
CREATE TABLE Inscricao (
aluno_id INT,
disciplina_id INT,
ano_lectivo VARCHAR(9),
nota DECIMAL(4,2) CHECK (nota >= 0 AND nota <= 20),
PRIMARY KEY (aluno_id, disciplina_id, ano_lectivo),
FOREIGN KEY (aluno_id) REFERENCES Aluno(id) ON DELETE CASCADE,
FOREIGN KEY (disciplina_id) REFERENCES Disciplina(id) ON DELETE RESTRICT
);
-- Aluno-Disciplina é N:M com atributos próprios (nota, ano).
c) Restrições importantes:
- Aluno.numero UNIQUE NOT NULL — número de aluno único.
- Disciplina.codigo UNIQUE NOT NULL.
- Inscricao.nota CHECK (nota >= 0 AND nota <= 20) — escala portuguesa.
- Inscricao PK composta com ano_lectivo permite aluno repetir disciplina noutro ano.
- Aluno.data_nascimento pode adicionar CHECK (data_nascimento < CURRENT_DATE).