Normalização

Bases de Dados

02

Aviso Importante

Este é um material de suporte não oficial, desenvolvido por alunos com o auxílio de inteligência artificial. Não representa a posição oficial da instituição ou dos professores.
Não questione ou incomode os professores sobre este conteúdo. O material é baseado em anotações feitas durante as aulas e consultas complementares feitas pelos próprios alunos.
Este material pode conter imprecisões. Em caso de dúvida, consulte sempre o material oficial fornecido pelo professor e as referências listadas ao final.
Sugestões e Correções
Encontrou um erro ou tem uma sugestão de melhoria? Envie sua contribuição pelo grupo do WhatsApp da turma. Toda colaboração é bem-vinda para melhorar o material para todos.
03
ÍNDICE

Conteúdo

04
INTRODUÇÃO

O que é Normalização?

Imagine uma planilha onde uma célula contém múltiplos valores separados por barras — como armazenar vários números de itens, descrições e preços todos em uma única linha. Isso é uma tabela não normalizada, e torna a atualização, busca e manutenção de dados um pesadelo. Normalização é o processo passo a passo de reorganizar essa tabela bagunçada em um conjunto de tabelas bem estruturadas que eliminam a redundância e protegem a integridade dos dados.

O processo segue uma série de formas normais, cada uma baseada na anterior. A Primeira Forma Normal (1NF) garante que cada célula contenha um único valor atômico — sem grupos repetitivos. A Segunda Forma Normal (2NF) remove dependências parciais, onde um atributo não-chave depende de apenas parte de uma chave composta. A Terceira Forma Normal (3NF) remove dependências transitivas, onde um atributo não-chave depende de outro atributo não-chave em vez da chave primária.

Pense nisso como organizar um arquivo bagunçado: a 1NF garante que cada gaveta tenha um tipo de documento, a 2NF garante que os documentos estejam arquivados na categoria correta, e a 3NF move informações com referência cruzada para sua própria gaveta dedicada. No final, cada dado existe em exatamente um lugar — atualize uma vez e estará correto em todo lugar.

Nesta aula, vamos percorrer dois exemplos completos — um sistema de pedidos de hardware de computador e um sistema de visitas hospitalares — transformando cada um de uma única tabela caótica em um conjunto limpo de tabelas normalizadas em 3NF. Ao longo do caminho, você aprenderá a identificar chaves primárias, chaves estrangeiras, chaves compostas e as dependências que conduzem cada etapa de normalização.

05
DIAGRAMA

UNF para 1NF — Removendo Grupos Repetitivos

Exemplo de pedido de hardware: um valor por célula, um fato por linha

O problema: Uma empresa de hardware de computador armazena pedidos em uma única tabela. Quando um pedido tem múltiplos itens, os detalhes dos itens são amontoados em uma célula separados por barras — X3412/X2189. Isso viola a 1NF porque cada célula deve conter exatamente um valor.

Como identificar: Observe cada célula da sua tabela. Alguma célula contém mais de um valor (separados por vírgulas, barras ou listados verticalmente)? Se sim, esses atributos formam um grupo repetitivo. A correção é sempre a mesma: mova os atributos repetitivos para uma nova tabela, dê um nome significativo e vincule usando a chave primária original como chave estrangeira.
✗ Antes — UNF (grupos repetitivos em laranja)
Order NoDateCust NoNameItem NoDescriptionCostQty
100105-Feb0123AmalX3412/X2189120Gb HD/Cisco NIC40/205/25
100210-Feb0345AliY7674/B3456/F6758417" Monitor/Wireless Mouse/2GB Ram50/10/806/10/7
⬇ Primeiro: expandir cada valor em sua própria linha
1NF — Todos os valores atômicos (um por célula)
Order NoDateCust NoNameAddressContactItem NoDescriptionCostQty
100105-Feb0123AmalDublin39112200X3412120Gb HD405
100105-Feb0123AmalDublin39112200X2189Cisco NIC2025
100210-Feb0345AliMeath3321001Y767417" Monitor506
100210-Feb0345AliMeath3321001B3456Wireless Mouse1010
100210-Feb0345AliMeath3321001F675842GB Ram807
Observe a redundância: Os detalhes de Amal (nome, endereço, contato) se repetem para cada item no pedido 1001. Os detalhes de Ali se repetem três vezes no pedido 1002. Agora está em 1NF (um valor por célula), mas os dados repetidos desperdiçam espaço e criam problemas de atualização. Próximo passo: dividir em ORDER (dados do pedido) e ORDER_ITEM (dados do item por pedido), vinculados pelo Número do Pedido como chave estrangeira.
⬇ Dividir em duas tabelas para reduzir redundância
✓ Tabela ORDER
Order No (PK)DateCust NoNameAddressContact
100105-Feb-20240123AmalDublin39112200
100210-Feb-20240345AliMeath3321001
✓ Tabela ORDER_ITEM (nova)
Order No (FK)Item No (PK)DescriptionCostQty
1001X3412120Gb HD405
1001X2189Cisco NIC2025
1002Y767417" Monitor506
1002B3456Wireless Mouse1010
1002F675842GB Ram807
Resultado: A chave composta de ORDER_ITEM é (Número do Pedido + Número do Item) — nenhum deles sozinho pode identificar uma linha de forma única. O Número do Pedido também é uma chave estrangeira vinculando de volta ao ORDER.

As colunas repetitivas de itens (laranja) são movidas para sua própria tabela. Cada item agora tem sua própria linha, e as duas tabelas são vinculadas pelo Número do Pedido.

06
DIAGRAMA

1NF para 2NF — Removendo Dependências Parciais

Exemplo de pedido de hardware: Descrição e Custo Unitário dependem apenas do Número do Item

O problema: Após a 1NF, ORDER_ITEM tem uma chave composta (Número do Pedido + Número do Item). Mas Descrição e Custo Unitário dependem apenas do Número do Item — não têm nada a ver com qual pedido é. Isso é chamado de dependência parcial.

Como identificar: Primeiro, verifique se alguma tabela tem uma chave composta (duas ou mais colunas formando a chave primária). Se tiver, pergunte a si mesmo para cada atributo não-chave: "Este atributo precisa de AMBAS as partes da chave para ser identificado, ou apenas uma?" Se a resposta for "apenas uma", esse atributo tem uma dependência parcial e deve ser movido para sua própria tabela. Aqui, "Qual o nome do item X3412?" só precisa do Número do Item — não importa a qual pedido pertence.
✗ Dependência Parcial Detectada
COMPOSITE KEY
Order No + Item No
DEPENDE APENAS DO ITEM NO ✗
Description, Unit Cost
|
DEPENDE DA CHAVE COMPLETA ✓
Quantity
⬇ Aplicar 2NF: mover atributos parcialmente dependentes
✓ ORDER_ITEM (atualizado)
Order No (FK)Item No (FK)Qty
1001X34125
1001X218925
1002Y76746
1002B345610
1002F675847
✓ ITEM (nova tabela)
Item No (PK)DescriptionUnit Cost
X3412120Gb HD40
X2189Cisco NIC20
Y767417" Monitor50
B3456Wireless Mouse10
F675842GB Ram80
Resultado: Agora temos três tabelas — ORDER, ORDER_ITEM e ITEM. O Número do Item se torna uma chave estrangeira em ORDER_ITEM referenciando a tabela ITEM. Nenhum atributo depende mais de apenas parte de uma chave.

Descrição e Custo Unitário são movidos para ITEM porque dependem apenas do Número do Item, não da chave composta completa.

07
DIAGRAMA

2NF para 3NF — Removendo Dependências Transitivas

Exemplo de pedido de hardware: Nome, Endereço e Contato dependem do Número do Cliente, não do Número do Pedido

O problema: A tabela ORDER ainda contém Nome, Endereço e Número de Contato. Esses atributos não dependem do Número do Pedido (a chave primária) — eles dependem do Número do Cliente, que é um atributo não-chave. Essa cadeia é chamada de dependência transitiva: Número do Pedido → Número do Cliente → Nome/Endereço/Contato.

Como identificar: Para cada atributo não-chave, pergunte: "Este atributo descreve a chave primária diretamente, ou descreve outro atributo não-chave?" Se Nome descreve o Número do Cliente (não o Número do Pedido), você tem uma dependência transitiva. O teste prático: "Se eu mudar esse valor, estaria errado em outras linhas?" Se Amal mudar de Dublin para Cork, cada pedido que ela fez mostraria o endereço errado — isso significa que o endereço pertence à sua própria tabela.
✗ Cadeia de Dependência Transitiva
PRIMARY KEY
Order No
NON-KEY
Customer No
DEPENDEM DO CUST NO ✗
Name, Address, Contact
⬇ Aplicar 3NF: mover atributos transitivamente dependentes
✓ ORDER (atualizado)
Order No (PK)Cust No (FK)Order Date
1001012305-Feb-2024
1002034510-Feb-2024
✓ CUSTOMER (nova tabela)
Cust No (PK)NameAddressContact
0123AmalDublin39112200
0345AliMeath3321001
✓ Esquema final — 4 tabelas em 3NF
ORDER
Order No (PK)Cust No (FK)Date
1001012305-Feb-2024
1002034510-Feb-2024
CUSTOMER
Cust No (PK)NameAddressContact
0123AmalDublin39112200
0345AliMeath3321001
ITEM
Item No (PK)DescriptionCost
X3412120Gb HD40
X2189Cisco NIC20
Y767417" Monitor50
B3456Wireless Mouse10
F675842GB Ram80
ORDER_ITEM
Order No (FK)Item No (FK)Qty
1001X34125
1001X218925
1002Y76746
1002B345610
1002F675847
Resultado: Quatro tabelas, cada uma armazenando um tipo de fato. Se Amal mudar de endereço, atualize uma linha em CUSTOMER. Se o preço de um item mudar, atualize uma linha em ITEM. Nenhuma redundância resta.

O resultado final em 3NF: ORDER, CUSTOMER, ITEM e ORDER_ITEM — quatro tabelas sem redundância, sem dependências parciais e sem dependências transitivas.

08
DIAGRAMA

Pedido de Hardware — Relacionamentos entre Tabelas

Como as quatro tabelas em 3NF se conectam através de chaves estrangeiras

1N1NN1CUSTOMERcustomer_numberPKnameaddresscontact_numberORDERorder_numberPKcustomer_numberFKorder_dateORDER_ITEMorder_numberFKitem_numberFKquantityITEMitem_numberPKdescriptionunit_cost
09
DIAGRAMA

Sistema de Visitas Hospitalares — Passo a Passo

Aplicando o checklist de três passos a dados de saúde

Segundo exemplo — Visitas hospitalares: Um hospital registra visitas de pacientes com detalhes de tratamentos. Na tabela original, uma linha armazena todos os tratamentos de uma visita (ex. "Raio-X, Dr Smith, Radiologia, €120; Exame de Sangue, Dr John, Patologia, €50"). Após aplicar 1NF (valores atômicos), 2NF (sem dependências parciais) e 3NF (sem dependências transitivas), os dados se dividem em cinco tabelas limpas. Cada fato — nomes de pacientes, departamentos de médicos, custos de tratamentos — é armazenado em exatamente um lugar.
✗ UNF — Detalhes do tratamento compactados em uma célula
VisitIDPatientDateTreatments
V101Ali Khan2026-03-10X-Ray (Dr Smith, Radiology, €120), Blood Test (Dr John, Pathology, €50)
V102Sara Noor2026-03-11MRI Scan (Dr Lee, Imaging, €300), ECG (Dr Patel, Cardiology, €90)
V103Emma Cole2026-03-12Consulta (Dr Patel, Cardiologia, €80), Raio-X (Dr Smith, Radiologia, €120)
Passo 1 — 1NF: A coluna Treatments tem múltiplos valores em uma célula. Como identificar: as vírgulas e parênteses nos dizem que nome do tratamento, médico, departamento e custo estão todos amontoados juntos. Correção: dar a cada tratamento sua própria linha. Chave composta = (VisitID, TreatmentName).
⬇ Resultado da 1NF
VisitIDPatientIDPatientNameDateTreatmentDoctorDeptCost
V101P01Ali Khan2026-03-10X-RayDr SmithRadiology€120
V101P01Ali Khan2026-03-10Blood TestDr JohnPathology€50
V102P02Sara Noor2026-03-11RessonânciaDr LeeImagiologia€300
V102P02Sara Noor2026-03-11ECGDr PatelCardiologia€90
V103P03Emma Cole2026-03-12ConsultaDr PatelCardiologia€80
V103P03Emma Cole2026-03-12Raio-XDr SmithRadiologia€120
Passo 2 — 2NF: A chave composta é (VisitID + TreatmentName). Pergunte para cada coluna: ela precisa de AMBAS as partes? PatientID, PatientName e Date só precisam de VisitID — são os mesmos independentemente do tratamento. Isso é uma dependência parcial. Correção: mover dados da visita/paciente para uma tabela VISITS.
⬇ Resultado da 2NF
VISITS
VisitIDPatientIDNameDate
V101P01Ali Khan2026-03-10
V102P02Sara Noor2026-03-11
V103P03Emma Cole2026-03-12
VISIT_TREATMENTS
VisitID (FK)TreatmentDoctorDeptCost
V101X-RayDr SmithRadiology€120
V101Blood TestDr JohnPathology€50
V102RessonânciaDr LeeImagiologia€300
V102ECGDr PatelCardiologia€90
V103ConsultaDr PatelCardiologia€80
V103Raio-XDr SmithRadiologia€120
Passo 3 — 3NF: Em VISITS, PatientName depende de PatientID (não de VisitID) — dependência transitiva. Em VISIT_TREATMENTS, Doctor e Dept descrevem o médico (não a visita), e Cost descreve o tratamento (não a visita). Pergunte: "Se o Dr Patel mudar de departamento, quantas linhas precisam ser atualizadas?" Múltiplas — então extraia. Correção: criar tabelas PATIENTS, DOCTORS e TREATMENTS.
⬇ Resultado da 3NF — cinco tabelas
Patients
PatientIDName
P01Ali Khan
P02Sara Noor
P03Emma Cole
Visits
VisitIDPatientID (FK)Date
V101P012026-03-10
V102P022026-03-11
V103P032026-03-12
Doctors
DoctorIDNameDept
D01Dr SmithRadiology
D02Dr JohnPathology
D03Dr LeeImaging
D04Dr PatelCardiology
Treatments
TreatmentIDNameCost
T01X-Ray€120
T02Blood Test€50
T03MRI Scan€300
T04ECG€90
T05Consultation€80
VisitTreatments
VisitID (FK)TreatmentID (FK)DoctorID (FK)
V101T01D01
V101T02D02
V102T03D03
V102T04D04
V103T05D04
V103T01D01
Ponto-chave: As mesmas três regras se aplicam independentemente do domínio. Pergunte a si mesmo: Este atributo depende da chave inteira, de apenas parte dela, ou de outro atributo não-chave? A resposta indica qual forma normal aplicar.

O exemplo hospitalar produz cinco tabelas em 3NF. Cada fato é armazenado uma vez — atualizar o departamento de um médico ou o custo de um tratamento requer a alteração de apenas uma linha.

10
DIAGRAMA

Sistema de Visitas Hospitalares — Relacionamentos entre Tabelas

Como as cinco tabelas em 3NF se conectam através de chaves estrangeiras

1N1N1NN1PATIENTSPatientIDPKPatientNameVISITSVisitIDPKPatientIDFKVisitDateDOCTORSDoctorIDPKDoctorNameDepartmentVISIT_TREATMENTSVisitIDFKTreatmentIDFKDoctorIDFKTREATMENTSTreatmentIDPKTreatmentNameCost
11
DIAGRAMA

Como Identificar Onde Dividir Tabelas

Um checklist de decisão em três passos para cada forma normal

A pergunta-chave em cada etapa: Normalização é sobre fazer uma pergunta por etapa. Se a resposta for "sim", você divide. Se "não", passe para a próxima etapa. Aqui está o checklist que você pode seguir para qualquer tabela em qualquer domínio.
1
Verificar 1NF — Existem grupos repetitivos?
Pergunte: Alguma célula contém mais de um valor?
Procure por: Barras (X3412/X2189), listas separadas por vírgula, múltiplas linhas de dados compactadas em uma célula.
Se SIM: Primeiro, expanda cada célula com múltiplos valores em sua própria linha — você obtém uma grande tabela plana onde cada célula é atômica. Então observe a redundância (dados repetidos entre linhas) e divida os atributos repetitivos em uma nova tabela. Use a PK original como chave estrangeira na nova tabela e crie uma chave composta (PK original + PK do novo atributo).
Se NÃO: A tabela já está em 1NF — prossiga para o passo 2.
2
Verificar 2NF — Alguma dependência parcial?
Pergunte: Existe uma chave composta? Algum atributo não-chave depende de apenas parte dela?
Teste: Para cada coluna não-chave, pergunte: "Preciso de AMBAS as partes da chave composta para consultar este valor?"
Se SIM (existe dep. parcial): Mova os atributos parcialmente dependentes para uma nova tabela, usando como chave a parte da qual dependem.
Se NÃO (ou sem chave composta): A tabela já está em 2NF — prossiga para o passo 3.
3
Verificar 3NF — Alguma dependência transitiva?
Pergunte: Algum atributo não-chave depende de outro atributo não-chave em vez da chave primária?
Teste: Para cada coluna não-chave, pergunte: "Isso descreve a PK diretamente, ou descreve uma coluna diferente?"
Teste prático: "Se eu atualizar esse valor, outras linhas ficariam inconsistentes?"
Se SIM (existe dep. transitiva): Mova os atributos transitivamente dependentes para uma nova tabela. Mantenha a coluna intermediária como FK.
Se NÃO: Parabéns — sua tabela está em 3NF!

Aplique essas três verificações em ordem a qualquer tabela. Cada passo é baseado no anterior — você deve alcançar a 1NF antes de verificar a 2NF, e a 2NF antes de verificar a 3NF.

12
DIAGRAMA

Exemplo Prático — Sistema de Cursos de Docentes

Normalizando um horário universitário de UNF para 3NF

Third example: Uma universidade armazena dados de docentes e cursos em uma tabela. Cada docente leciona múltiplos cursos, então os detalhes dos cursos se repetem. Vamos aplicar o mesmo checklist de três passos.
✗ UNF — Grupos repetitivos de cursos
Lect NoNameFacultyOfficeCourse CodeCourse NameStart DateWeekly Hrs
7146JamesBusinessRoom 22BM01Buss Manage12/9/20234
A01Account11/9/20233
E01Economics30/9/20237
1463DenisITRoom 21CS01Computer Systems01/9/20236
P01Programming02/9/20235
6455PhilBusinessRoom 22BM01Buss Manage04/10/20234
M01Marketing10/10/20236
⬇ Passo 1: Expandir cada valor em sua própria linha
1NF — Todos os valores atômicos (um por célula)
Lect NoNameFacultyOfficeCourse CodeCourse NameStartHrs
7146JamesBusinessRoom 22BM01Buss Manage12/9/234
7146JamesBusinessRoom 22A01Account11/9/233
7146JamesBusinessRoom 22E01Economics30/9/237
1463DenisITRoom 21CS01Comp Systems01/9/236
1463DenisITRoom 21P01Programming02/9/235
6455PhilBusinessRoom 22BM01Buss Manage04/10/234
6455PhilBusinessRoom 22M01Marketing10/10/236
Notice the redundancy: James's details (name, faculty, office) repeat for each of his 3 courses. Denis repeats twice, Phil repeats twice. This wastes space and creates update problems — if James changes office, three rows need updating. Next: split into LECTURER (lecturer data) and LECTURER_COURSE (course assignments), linked by Lect No as a foreign key.
⬇ Dividir em duas tabelas para reduzir redundância
✓ LECTURER
Lect No (PK)NameFacultyOffice
7146JamesBusinessRoom 22
1463DenisITRoom 21
6455PhilBusinessRoom 22
✓ LECTURER_COURSE (chave composta)
Lect No (FK)Course CodeCourse NameStartHrs
7146BM01Buss Manage12/9/234
7146A01Account11/9/233
7146E01Economics30/9/237
1463CS01Comp Systems01/9/236
1463P01Programming02/9/235
6455BM01Buss Manage04/10/234
6455M01Marketing10/10/236
⬇ Passo 2: Aplicar 2NF
Dependência parcial encontrada: Course Name depende apenas de Course Code (não da chave composta completa Lect No + Course Code). O mesmo curso sempre tem o mesmo nome independentemente de quem o leciona. Correção: extrair tabela COURSE.
✓ COURSE (new)
Code (PK)Name
BM01Buss Manage
A01Account
E01Economics
CS01Comp Systems
P01Programming
M01Marketing
✓ LECTURER_COURSE (atualizado)
Lect No (FK)Code (FK)Start DateWeekly Hrs
7146BM0112/9/234
7146A0111/9/233
7146E0130/9/237
1463CS0101/9/236
1463P0102/9/235
6455BM0104/10/234
6455M0110/10/236
⬇ Passo 3: Verificar 3NF
Check LECTURER: Do Faculty or Office depend on another non-key attribute? Faculty describes the lecturer directly, and Office is assigned to the lecturer — no transitive dependency. Result: LECTURER is already in 3NF.
✓ Esquema final — 3 tabelas em 3NF
LECTURER
Lect No (PK)NameFacultyOffice
7146JamesBusinessRoom 22
1463DenisITRoom 21
6455PhilBusinessRoom 22
COURSE
Code (PK)Name
BM01Buss Manage
A01Account
E01Economics
CS01Comp Systems
P01Programming
M01Marketing
LECTURER_COURSE
Lect No (FK)Code (FK)StartHrs
7146BM0112/9/234
7146A0111/9/233
7146E0130/9/237
1463CS0101/9/236
1463P0102/9/235
6455BM0104/10/234
6455M0110/10/236

O exemplo do docente usa o mesmo checklist de três passos. Course Name tinha uma dependência parcial de Course Code, mas nenhuma dependência transitiva foi encontrada — a 3NF foi alcançada no passo 2.

13
DIAGRAMA

Sistema de Cursos de Docentes — Relacionamentos entre Tabelas

Como as três tabelas em 3NF se conectam através de chaves estrangeiras

1NN1LECTURERLect_NoPKNameFacultyOfficeLECTURER_COURSELect_NoFKCourse_CodeFKStart_DateWeekly_HrsCOURSECourse_CodePKCourse_Name
14
CÓDIGO

SQL — Passo 1: UNF para 1NF

Dividir a tabela ORDER única em ORDER + ORDER_ITEM

step1_1nf.sql
-- STEP 1: UNF → 1NF
-- Remove repeating groups (item details) into a new table

CREATE TABLE "Order" (
    order_number     INT           PRIMARY KEY,
    order_date       DATE          NOT NULL,
    customer_number  VARCHAR(4)    NOT NULL,
    name             VARCHAR(50)   NOT NULL,
    address          VARCHAR(100)  NOT NULL,
    contact_number   VARCHAR(20)   NOT NULL
);

-- New table for the repeating item group
-- Composite key: order_number + item_number
CREATE TABLE Order_Item (
    order_number  INT          REFERENCES "Order",
    item_number   VARCHAR(10)  NOT NULL,
    description   VARCHAR(100) NOT NULL,
    unit_cost     DECIMAL(10,2) NOT NULL,
    quantity      INT          NOT NULL,
    PRIMARY KEY (order_number, item_number)
);
15
CÓDIGO

SQL — Passo 2: 1NF para 2NF

Extrair tabela ITEM para remover dependência parcial

step2_2nf.sql
-- STEP 2: 1NF → 2NF
-- Description and unit_cost depend only on item_number
-- (partial dependency on the composite key)
-- Move them to a new ITEM table

CREATE TABLE Item (
    item_number  VARCHAR(10)   PRIMARY KEY,
    description  VARCHAR(100)  NOT NULL,
    unit_cost    DECIMAL(10,2) NOT NULL
);

-- Order_Item now only keeps quantity
-- (depends on the FULL composite key)
CREATE TABLE Order_Item (
    order_number  INT          REFERENCES "Order",
    item_number   VARCHAR(10)  REFERENCES Item,
    quantity      INT          NOT NULL,
    PRIMARY KEY (order_number, item_number)
);
16
CÓDIGO

SQL — Passo 3: 2NF para 3NF

Extrair tabela CUSTOMER para remover dependência transitiva

step3_3nf.sql
-- STEP 3: 2NF → 3NF
-- Name, address and contact depend on customer_number
-- (transitive dependency — not on the PK)
-- Move them to a new CUSTOMER table

CREATE TABLE Customer (
    customer_number  VARCHAR(4)    PRIMARY KEY,
    name             VARCHAR(50)   NOT NULL,
    address          VARCHAR(100)  NOT NULL,
    contact_number   VARCHAR(20)   NOT NULL
);

-- Order now only keeps customer_number as FK
CREATE TABLE "Order" (
    order_number     INT           PRIMARY KEY,
    customer_number  VARCHAR(4)    REFERENCES Customer,
    order_date       DATE          NOT NULL
);

-- Final schema: 4 tables in 3NF
-- Customer, Order, Item, Order_Item
-- Each fact stored exactly once ✓
17
RESUMO

Referência Rápida

ConceitoRegraO que Fazer
UNFDados brutos, pode ter grupos repetitivosIdentificar entidade, listar todos os atributos, encontrar a chave
1NFSem atributos multivaloradosRemover grupos repetitivos para uma nova tabela; adicionar uma chave estrangeira para vinculá-los
2NFSem dependências parciaisSe existe uma chave composta, mover atributos que dependem de apenas parte dela
3NFSem dependências transitivasMover atributos não-chave que dependem de outros atributos não-chave
Chave Primária (PK)Identifica cada linha de forma únicaNão pode ser NULL; sublinhada na notação
Chave Estrangeira (FK)Referencia uma PK em outra tabelaCria relacionamentos entre tabelas
Chave CompostaDois ou mais atributos formando uma PKUsada quando nenhum atributo único é exclusivo (ex.: Nº Pedido + Nº Item)
18
EXERCÍCIOS

Exercícios Práticos

Teste seu entendimento sobre normalização

01
Uma tabela de docentes tem as colunas: Lecturer_no, Name, Faculty, Office_loc, Course_code, Course_name, Start_date, Weekly_time. Um docente leciona múltiplos cursos. Quais são os atributos repetitivos (multivalorados)?
Quais atributos têm múltiplos valores para um único docente?
02
Após remover o grupo repetitivo para uma tabela LECTURER_COURSE, qual seria a chave composta dessa nova tabela?
Quais dois atributos juntos identificam cada linha de forma única?
03
No exemplo de pedido de hardware, a tabela ORDER contém Name, Address e Contact Number junto com Customer Number. Que tipo de dependência Name, Address e Contact Number têm, e qual forma normal isso viola?
Esses atributos dependem da chave primária (Order Number) ou de outro atributo não-chave?
04
No exemplo de visita hospitalar, após alcançar a 1NF, a chave composta é (VisitID, TreatmentName). PatientName e VisitDate dependem apenas de VisitID. Qual forma normal isso viola e por quê?
PatientName depende da chave composta completa ou de apenas parte dela?
05
Qual é a diferença entre uma chave composta e uma chave estrangeira?
06
Dado: BOOK(BookID, BookDesc, CategoryID, CategoryDesc). Esta tabela está em 3NF? Se não, explique por que e mostre a correção.
Cada atributo não-chave depende diretamente de BookID?
07
Fácil — Normalize esta tabela de empréstimo de biblioteca para 3NF.
LoanIDMemberNameMemberEmailBookTitleAuthorLoanDateReturnDate
L01Jane Doejane@mail.comSQL BasicsT. Brown01/03/2615/03/26
L02Jane Doejane@mail.comData DesignR. Singh05/03/2619/03/26
L03Mark Leemark@mail.comSQL BasicsT. Brown10/03/2624/03/26
Quais atributos descrevem o membro? Quais descrevem o livro? Quais descrevem o empréstimo em si?
08
Médio — Normalize esta tabela de reservas de academia para 3NF.
BookingIDClientPhoneClassInstructorInst PhoneRoomDate
B01Anna555-1234YogaCoach Kim555-9999R110/03/26
B02Tom555-5678YogaCoach Kim555-9999R110/03/26
B03Anna555-1234PilatesCoach Lee555-8888R211/03/26
Phone depende de BookingID ou do cliente? Instructor Phone depende de BookingID ou do instrutor?
09
Médio-Difícil — Normalize esta tabela de loja online. Identifique todas as cadeias de dependência.
PurchIDCustEmailCustNameProductCategoryCatDiscountQtyDate
P01jo@mail.comJoKeyboardPeripherals10%201/03/26
P02jo@mail.comJoMonitorDisplays5%102/03/26
P03sam@mail.comSamMousePeripherals10%303/03/26
Procure por cadeias: CatDiscount depende de Category, que depende de Product?
10
Difícil — Esta tabela de hotel tem um grupo repetitivo. Normalize-a passando por todos os três passos até a 3NF.
ReservIDGuestGuestEmailCheckInRoomNoRoomTypeRateServices
R01Liamliam@mail.com01/03101Single€80Breakfast €15, Spa €40
R02Miamia@mail.com02/03205Double€120Breakfast €15, Gym €10, Laundry €25
R03Liamliam@mail.com10/03101Single€80Gym €10
Comece pela coluna Services — ela tem múltiplos valores. Depois procure o que descreve o hóspede versus o que descreve o quarto.
11
Desafio — Uma universidade armazena matrículas de alunos. Esta tabela tem grupos repetitivos E dependências transitivas. Normalize para 3NF mostrando cada passo.
StudentIDNameAddressDeptCodeDeptNameModules
S001AvaDublinCSComp SciM101 Databases (Dr Ryan, B+), M102 Networks (Dr Kelly, A)
S002NoahCorkCSComp SciM101 Databases (Dr Ryan, A-), M103 Web Dev (Dr Murphy, B)
S003EllaGalwayBABusinessM201 Marketing (Dr Walsh, A), M101 Databases (Dr Ryan, B+)
A coluna Modules tem código do módulo, nome, docente e nota todos agrupados. Após separar, pergunte: o nome do módulo depende do aluno, ou do código do módulo? A nota depende de uma chave ou de duas?
19
RESPOSTAS

Respostas

01
Course_code, Course_name, Start_date, Weekly_time
Esses quatro atributos se repetem para cada curso que um docente leciona — eles formam o grupo repetitivo que viola a 1NF.
02
(Lecturer_no, Course_code)
Nenhum atributo sozinho pode identificar uma linha de forma única — Lecturer_no se repete para cada curso, e Course_code se repete se múltiplos docentes lecionam o mesmo curso. Juntos, são únicos.
03
Dependência transitiva — viola a 3NF
Name, Address e Contact Number dependem de Customer Number, que é um atributo não-chave. A correção é extrair uma tabela CUSTOMER com Customer Number como PK, e manter apenas Customer Number como FK em ORDER.
04
2NF — dependência parcial
PatientName depende apenas de VisitID, não da chave composta completa (VisitID + TreatmentName). A correção é mover os dados do paciente para uma tabela Visits separada.
05
Uma chave composta são dois ou mais atributos que juntos formam uma chave primária (ex.: Nº Pedido + Nº Item em ORDER_ITEM). Uma chave estrangeira é um atributo que referencia a chave primária de outra tabela para criar um relacionamento (ex.: Customer Number em ORDER referencia CUSTOMER).
Uma chave composta identifica linhas dentro de sua própria tabela; uma chave estrangeira vincula a linhas em outra tabela. Elas servem a propósitos diferentes — identificação versus relacionamento.
06
Não está em 3NF. CategoryDesc depende de CategoryID (dependência transitiva), não diretamente de BookID. Correção: dividir em BOOK(BookID, BookDesc, CategoryID FK) e CATEGORY(CategoryID, CategoryDesc).
BookID → CategoryID → CategoryDesc é uma cadeia de dependências. Na 3NF, todo atributo não-chave deve depender apenas da chave primária, sem intermediários.
07
Tabela Original
LoanIDMemberNameMemberEmailBookTitleAuthorLoanDateReturnDate
L01Jane Doejane@mail.comSQL BasicsT. Brown01/03/2615/03/26
L02Jane Doejane@mail.comData DesignR. Singh05/03/2619/03/26
L03Mark Leemark@mail.comSQL BasicsT. Brown10/03/2624/03/26
Passo 1 — 1NF: Verifique cada célula — alguma contém múltiplos valores? Não, cada célula tem exatamente um valor. ✓ Já está em 1NF.
⬇ A tabela continua a mesma
Verificação do passo 1 — tabela inalterada
LoanIDMemberNameMemberEmailBookTitleAuthorLoanDateReturnDate
L01Jane Doejane@mail.comSQL BasicsT. Brown01/03/2615/03/26
L02Jane Doejane@mail.comData DesignR. Singh05/03/2619/03/26
L03Mark Leemark@mail.comSQL BasicsT. Brown10/03/2624/03/26
Passo 2 — 2NF: Existe uma chave composta? Não — a chave primária é LoanID (coluna única). Dependências parciais só ocorrem com chaves compostas. ✓ Já está em 2NF.
⬇ A tabela continua a mesma
Verificação do passo 2 — tabela inalterada
LoanIDMemberNameMemberEmailBookTitleAuthorLoanDateReturnDate
L01Jane Doejane@mail.comSQL BasicsT. Brown01/03/2615/03/26
L02Jane Doejane@mail.comData DesignR. Singh05/03/2619/03/26
L03Mark Leemark@mail.comSQL BasicsT. Brown10/03/2624/03/26
Passo 3 — 3NF: For each non-key column, ask: "does this describe the PK or another column?"
• MemberName & MemberEmail depend on the member, não de LoanID — dependência transitiva.
• BookTitle & Author depend on the book, não de LoanID — dependência transitiva.
Correção: extract MEMBER and BOOK tables.
⬇ Resultado da 3NF — três tabelas
MEMBER
MemberIDMemberNameMemberEmail
M01Jane Doejane@mail.com
M02Mark Leemark@mail.com
BOOK
BookIDBookTitleAuthor
B01SQL BasicsT. Brown
B02Data DesignR. Singh
LOAN
LoanIDMemberID (FK)BookID (FK)LoanDateReturnDate
L01M01B0101/03/2615/03/26
L02M01B0205/03/2619/03/26
L03M02B0110/03/2624/03/26
Resultado: 3 tabelas em 3NF. Cada fato armazenado uma vez — mudar o email de Jane atualiza exatamente uma linha em MEMBER.
Relacionamentos entre Tabelas
1NN1MEMBERMemberIDPKMemberNameMemberEmailLOANLoanIDPKMemberIDFKBookIDFKLoanDateReturnDateBOOKBookIDPKBookTitleAuthor
08
Tabela Original
BookingIDClientPhoneClassInstructorInst PhoneRoomDate
B01Anna555-1234YogaCoach Kim555-9999R110/03/26
B02Tom555-5678YogaCoach Kim555-9999R110/03/26
B03Anna555-1234PilatesCoach Lee555-8888R211/03/26
Passo 1 — 1NF: Verifique cada célula — alguma contém múltiplos valores? Não, cada célula tem exatamente um valor. ✓ Já está em 1NF.
⬇ A tabela continua a mesma
Verificação do passo 1 — tabela inalterada
BookingIDClientPhoneClassInstructorInst PhoneRoomDate
B01Anna555-1234YogaCoach Kim555-9999R110/03/26
B02Tom555-5678YogaCoach Kim555-9999R110/03/26
B03Anna555-1234PilatesCoach Lee555-8888R211/03/26
Passo 2 — 2NF: Existe uma chave composta? Não — a chave primária é BookingID (coluna única). Dependências parciais só ocorrem com chaves compostas. ✓ Já está em 2NF.
⬇ A tabela continua a mesma
Verificação do passo 2 — tabela inalterada
BookingIDClientPhoneClassInstructorInst PhoneRoomDate
B01Anna555-1234YogaCoach Kim555-9999R110/03/26
B02Tom555-5678YogaCoach Kim555-9999R110/03/26
B03Anna555-1234PilatesCoach Lee555-8888R211/03/26
Passo 3 — 3NF: For each non-key column, ask: "does this describe the PK or another column?"
• Phone depends on Client, not on BookingID — transitive dependency (Anna's phone repeats in B01 & B03).
• Instructor, Inst Phone & Room depend on Class, not on BookingID — transitive dependency (Yoga is always Coach Kim in R1).
• Inst Phone depends on Instructor, not on Class — another transitive dependency (Coach Kim's phone describes the instructor).
Correção: extract CLIENT, INSTRUCTOR, and CLASS tables.
⬇ Resultado da 3NF — quatro tabelas
CLIENT
ClientIDClientNamePhone
C01Anna555-1234
C02Tom555-5678
INSTRUCTOR
InstructorIDNamePhone
I01Coach Kim555-9999
I02Coach Lee555-8888
CLASS
ClassIDClassNameInstructorID (FK)Room
CL01YogaI01R1
CL02PilatesI02R2
BOOKING
BookingIDClientID (FK)ClassID (FK)Date
B01C01CL0110/03/26
B02C02CL0110/03/26
B03C01CL0211/03/26
Resultado: 4 tabelas em 3NF. Cada fato armazenado uma vez — mudar o telefone de Coach Kim atualiza uma linha em INSTRUCTOR.
Relacionamentos entre Tabelas
1NN1N1CLIENTClientIDPKClientNamePhoneBOOKINGBookingIDPKClientIDFKClassIDFKDateCLASSClassIDPKClassNameInstructorIDFKRoomINSTRUCTORInstructorIDPKNamePhone
09
Tabela Original
PurchIDCustEmailCustNameProductCategoryCatDiscountQtyDate
P01jo@mail.comJoKeyboardPeripherals10%201/03/26
P02jo@mail.comJoMonitorDisplays5%102/03/26
P03sam@mail.comSamMousePeripherals10%303/03/26
Passo 1 — 1NF: Verifique cada célula — alguma contém múltiplos valores? Não, cada célula tem exatamente um valor. ✓ Já está em 1NF.
⬇ A tabela continua a mesma
Verificação do passo 1 — tabela inalterada
PurchIDCustEmailCustNameProductCategoryCatDiscountQtyDate
P01jo@mail.comJoKeyboardPeripherals10%201/03/26
P02jo@mail.comJoMonitorDisplays5%102/03/26
P03sam@mail.comSamMousePeripherals10%303/03/26
Passo 2 — 2NF: Existe uma chave composta? Não — a chave primária é PurchID (coluna única). Dependências parciais só ocorrem com chaves compostas. ✓ Já está em 2NF.
⬇ A tabela continua a mesma
Verificação do passo 2 — tabela inalterada
PurchIDCustEmailCustNameProductCategoryCatDiscountQtyDate
P01jo@mail.comJoKeyboardPeripherals10%201/03/26
P02jo@mail.comJoMonitorDisplays5%102/03/26
P03sam@mail.comSamMousePeripherals10%303/03/26
Passo 3 — 3NF: For each non-key column, ask: "does this describe the PK or another column?"
• CustName depends on CustEmail, not on PurchID — transitive dependency (Jo's name repeats whenever jo@mail.com appears).
• Category depends on Product, not on PurchID — transitive dependency (Keyboard is always Peripherals).
• CatDiscount depends on Category, not on Product — this is a cadeia transitiva dupla: PurchID → Product → Category → CatDiscount (10% belongs to all Peripherals, not per product).
Correção: extract CUSTOMER, CATEGORY, and PRODUCT tables.
⬇ Resultado da 3NF — quatro tabelas
CUSTOMER
CustIDCustEmailCustName
C01jo@mail.comJo
C02sam@mail.comSam
CATEGORY
CatIDCatNameCatDiscount
CAT01Peripherals10%
CAT02Displays5%
PRODUCT
ProdIDProdNameCatID (FK)
PR01KeyboardCAT01
PR02MonitorCAT02
PR03MouseCAT01
PURCHASE
PurchIDCustID (FK)ProdID (FK)QtyDate
P01C01PR01201/03/26
P02C01PR02102/03/26
P03C02PR03303/03/26
Resultado: 4 tabelas em 3NF. Cada fato armazenado uma vez — mudar o desconto de Periféricos de 10% para 15% atualiza uma linha em CATEGORY.
Relacionamentos entre Tabelas
1NN1N1CUSTOMERCustIDPKCustEmailCustNamePURCHASEPurchIDPKCustIDFKProdIDFKQtyDatePRODUCTProdIDPKProdNameCatIDFKCATEGORYCatIDPKCatNameCatDiscount
10
Tabela Original
ReservIDGuestGuestEmailCheckInRoomNoRoomTypeRateServices
R01Liamliam@mail.com01/03101Single€80Breakfast €15, Spa €40
R02Miamia@mail.com02/03205Double€120Breakfast €15, Gym €10, Laundry €25
R03Liamliam@mail.com10/03101Single€80Gym €10
Passo 1 — 1NF: A coluna Services tem múltiplos valores (Breakfast €15, Spa €40). Isso é um grupo repetitivo. Correção: mover serviços para uma nova tabela RESERVATION_SERVICE com chave composta (ReservID + ServiceName).
⬇ Resultado da 1NF
1NF — Todos os valores atômicos (um por célula)
ReservIDGuestGuestEmailCheckInRoomNoRoomTypeRateServiceNameServicePrice
R01Liamliam@mail.com01/03101Single€80Breakfast€15
R01Liamliam@mail.com01/03101Single€80Spa€40
R02Miamia@mail.com02/03205Double€120Breakfast€15
R02Miamia@mail.com02/03205Double€120Gym€10
R02Miamia@mail.com02/03205Double€120Laundry€25
R03Liamliam@mail.com10/03101Single€80Gym€10
Observe a redundância: Os detalhes de Liam se repetem para cada serviço. O tipo e tarifa do Room 101 se repetem. O preço do Breakfast (€15) aparece duas vezes. Next: dividir em RESERVATION + RESERVATION_SERVICE, vinculados por ReservID.
⬇ Dividir em tabelas separadas
RESERVATION (após 1NF)
ReservIDGuestGuestEmailCheckInRoomNoRoomTypeRate
R01Liamliam@mail.com01/03101Single€80
R02Miamia@mail.com02/03205Double€120
R03Liamliam@mail.com10/03101Single€80
RESERVATION_SERVICE (após 1NF)
ReservIDServiceNamePrice
R01Breakfast€15
R01Spa€40
R02Breakfast€15
R02Gym€10
R02Laundry€25
R03Gym€10
Passo 2 — 2NF: RESERVATION_SERVICE tem chave composta (ReservID, ServiceName). Price depende apenas de ServiceName (Breakfast é sempre €15) — dependência parcial. Correção: extrair tabela SERVICE.
⬇ Resultado da 2NF
SERVICE
ServiceIDServiceNamePrice
SV01Breakfast€15
SV02Spa€40
SV03Gym€10
SV04Laundry€25
RESERVATION_SERVICE (após 2NF)
ReservID (FK)ServiceID (FK)
R01SV01
R01SV02
R02SV01
R02SV03
R02SV04
R03SV03
Passo 3 — 3NF: Em RESERVATION: Guest & GuestEmail dependem do hóspede (não de ReservID) — transitiva. RoomType & Rate dependem de RoomNo (não de ReservID) — transitiva. Correção: extrair tabelas GUEST e ROOM.
⬇ Resultado da 3NF — cinco tabelas
GUEST
GuestIDGuestNameGuestEmail
G01Liamliam@mail.com
G02Miamia@mail.com
ROOM
RoomNoRoomTypeRate
101Single€80
205Double€120
RESERVATION
ReservIDGuestID (FK)RoomNo (FK)CheckIn
R01G0110101/03
R02G0220502/03
R03G0110110/03
RESERVATION_SERVICE
ReservID (FK)ServiceID (FK)
R01SV01
R01SV02
R02SV01
R02SV03
R02SV04
R03SV03
Resultado: 5 tabelas em 3NF. Todos os três passos usados: 1NF (grupo repetitivo), 2NF (dependência parcial), 3NF (duas cadeias transitivas). Cada fato armazenado uma vez.
Relacionamentos entre Tabelas
1NN11NN1GUESTGuestIDPKGuestNameGuestEmailRESERVATIONReservIDPKGuestIDFKRoomNoFKCheckInROOMRoomNoPKRoomTypeRateRESERV_SERVICEReservIDFKServiceIDFKSERVICEServiceIDPKServiceNamePrice
11
Tabela Original
StudentIDNameAddressDeptCodeDeptNameModules
S001AvaDublinCSComp SciM101 Databases (Dr Ryan, B+), M102 Networks (Dr Kelly, A)
S002NoahCorkCSComp SciM101 Databases (Dr Ryan, A-), M103 Web Dev (Dr Murphy, B)
S003EllaGalwayBABusinessM201 Marketing (Dr Walsh, A), M101 Databases (Dr Ryan, B+)
Passo 1 — 1NF: A coluna Modules agrupa múltiplos valores (código, nome, docente, nota por módulo). Grupo repetitivo encontrado. Correção: criar tabela ENROLMENT. Chave composta = (StudentID + ModCode).
⬇ Resultado da 1NF
1NF — Todos os valores atômicos (um por célula)
StudentIDNameAddressDeptCodeDeptNameModCodeModNameLecturerGrade
S001AvaDublinCSComp SciM101DatabasesDr RyanB+
S001AvaDublinCSComp SciM102NetworksDr KellyA
S002NoahCorkCSComp SciM101DatabasesDr RyanA-
S002NoahCorkCSComp SciM103Web DevDr MurphyB
S003EllaGalwayBABusinessM201MarketingDr WalshA
S003EllaGalwayBABusinessM101DatabasesDr RyanB+
Observe a redundância: Os detalhes de Ava se repetem para cada módulo. DeptName 'Comp Sci' se repete para cada aluno de CS. O módulo M101 'Databases' e 'Dr Ryan' se repetem para cada aluno matriculado. Next: dividir em STUDENT + ENROLMENT, vinculados por StudentID.
⬇ Dividir em tabelas separadas
STUDENT (após 1NF)
StudentIDNameAddressDeptCodeDeptName
S001AvaDublinCSComp Sci
S002NoahCorkCSComp Sci
S003EllaGalwayBABusiness
ENROLMENT (após 1NF)
StudentIDModCodeModNameLecturerGrade
S001M101DatabasesDr RyanB+
S001M102NetworksDr KellyA
S002M101DatabasesDr RyanA-
S002M103Web DevDr MurphyB
S003M201MarketingDr WalshA
S003M101DatabasesDr RyanB+
Passo 2 — 2NF: Composite key (StudentID + ModCode). Ask: does ModName need both keys? No — M101 is always "Databases" regardless of which student. Same for Lecturer. Dependência parcial. Correção: extrair tabela MODULE. ENROLMENT keeps only Grade (depends on both keys).
⬇ Resultado da 2NF
MODULE
ModCodeModNameLecturer
M101DatabasesDr Ryan
M102NetworksDr Kelly
M103Web DevDr Murphy
M201MarketingDr Walsh
ENROLMENT (após 2NF)
StudentID (FK)ModCode (FK)Grade
S001M101B+
S001M102A
S002M101A-
S002M103B
S003M201A
S003M101B+
Passo 3 — 3NF: Em STUDENT: DeptName depende de DeptCode (não de StudentID) — dependência transitiva. CS é sempre "Comp Sci". Correção: extrair tabela DEPARTMENT.
⬇ Resultado da 3NF — quatro tabelas
DEPARTMENT
DeptCodeDeptName
CSComp Sci
BABusiness
STUDENT
StudentIDNameAddressDeptCode (FK)
S001AvaDublinCS
S002NoahCorkCS
S003EllaGalwayBA
MODULE
ModCodeModNameLecturer
M101DatabasesDr Ryan
M102NetworksDr Kelly
M103Web DevDr Murphy
M201MarketingDr Walsh
ENROLMENT
StudentID (FK)ModCode (FK)Grade
S001M101B+
S001M102A
S002M101A-
S002M103B
S003M201A
S003M101B+
Resultado: 4 tables in 3NF. All three steps used: 1NF (repeating modules), 2NF (partial dep on ModCode), 3NF (transitive dep on DeptCode). Grade is the only attribute that genuinely needs both parts of the composite key.
Relacionamentos entre Tabelas
1N1NN1DEPARTMENTDeptCodePKDeptNameSTUDENTStudentIDPKNameAddressDeptCodeFKENROLMENTStudentIDFKModCodeFKGradeMODULEModCodePKModNameLecturer
20
REFERÊNCIAS

Referências

Fontes utilizadas na elaboração deste material

01
Database Normalization: 1NF, 2NF, 3NF & BCNF Examples
Tutorial abrangente com exemplos visuais para cada forma normal
Acessado em: Acesso em 22 de março de 2026
02
Database Normalization – Normal Forms 1nf 2nf 3nf Table Examples
Passo a passo de normalização com exemplos práticos de tabelas
Acessado em: Acesso em 22 de março de 2026
03
Normal Forms in DBMS
Guia de referência cobrindo todas as formas normais com definições e exemplos
Acessado em: Acesso em 22 de março de 2026
04
DBMS Normalization: 1NF, 2NF, 3NF Database Example
Tutorial de normalização para iniciantes com diagramas e exemplos SQL
Acessado em: Acesso em 22 de março de 2026