Normalização de Banco de Dados
O que é?
A normalização se baseia em dois pilares:
Formalização
╭──────────────────────────╮───────────────────────────╮
│ Menos │ Menos │
│ Dependência │ Redundância │
│ ╱ ╲ │ ╱ ╲ │
│ ╱ ╲ │ ╱ ╲ │
│ ╱ ╲ │ ╱ ╲ │
│ ╱ ╲ │ ╱ ╲ │
│ ╱ ╲ │ ╱ ╲ │
│ ╱ ╲ │ ╱ ╲ │
│ ╱ ╲ │ ╱ ╲ │
│ ╱ ╲ │ ╱ ╲ │
│ ╱ ╲ │ ╱ ╲ │
│╱ ╲ │╱ ╲ │
╰──────────────────────────╯───────────────────────────╯
Ou seja, a normalização de uma base de dados (tabelas) visa deixar o banco menos:
- redundante => muitas informações iguais
- dependência => menos dependência entre atributos Assim devemos fazer a verificação (ou seja, a normalização) no modelo lógico.
Dependências
As dependências são importantes conceitos que deve ser entendido e se trata daquela musica que o Cirilo canta: "Não existo longe de você"; É a mesma coisa que acontece quando um campo tem dependência em outro
Dependência Funcional
É dito quando tenho um atributo A e um B, tal que o B depende funcionalmente
Nessa dependência um atributo faz com que todos os outros sejam dependentes dele, por exemplo, em uma dependência funcional (DF) de uma entidade (ou tabela) alunos temos:
$$ Matricula \to Nome $$ $$ Matricula \to Sexo $$ $$ Matricula \to Celular $$
Assim, precisamos saber a matricula para saber os atributos Nome, Sexo e Celular do Aluno
O inverso não funciona, já que sexo, celular e nem nome identifica a matricula do aluno ❗Lembre-se que a Matricula é um identificador que consegue identificar outros atributos
Dependência Funcional Parcial
É quando temos uma chave composta e os atributos tem uma dependência por parte da chave.
Por exemplo, para os dados de médicos:
$CRM + SinglaEstado \to Nome, Sexo, EstadoEmissor$
Note que a chave composta é: CRM + SiglaEstado. E o campo EstadoEmissor
depende parcialmente da chave composta já que só precisa da SiglaEstado
💡 Quando temos apenas uma chave primaria simples, ela não terá dependência funcional parcial
Dependência Funcional Transitiva
É definido como uma dependência de um atributo não chave com outro atributo não chave.
Exemplo de DFT com dados de alunos:
$Matricula \to Nome, CodCurso, NomeCurso$
Tal que o campo não chave
NomeCurso
depende diretamente do campo não chaveCodCurso
O campo chave neste exemplo é aMatricula
Formas
Existem cinco formas e elas são um conjunto de passos para aplicar nas tabelas (modelo lógico).
💡A 4FN e a 5FN não são usadas muito
Antes de tudo
Vamos praticar com um exemplo de um sistema para uma universidade
Departamento | ||
---|---|---|
PK | COD_DEPARTAMENTO | NUMERO |
NOM_DEPARTAMENTO | CARACTERE |
Professor | ||
---|---|---|
PK | COD_PROFESSOR | NUMERO |
NOME_PROFESSOR | CARACTERE | |
SOBRENOME_PROFESSOR | CARACTERE | |
STATUS | BOOLEAN | |
FK | COD_DEPARTAMENTO | NUMERO |
No campo
COD_DEPARTAMENTO
dependendo dos requisitos pode ser que um professor faça parte de um ou mais departamento, logo teríamos um atributo multivalorado e esse tipo de atributo não respeita a primeira forma, assim teria que aplicar a 1FN
Turma | ||
---|---|---|
PK | COD_TURMA | NUMERO |
PERIODO | CARACTERE | |
NUMERO_ALUNOS | NUMERO | |
DATA_INICIO | DATA | |
DATA_FIM | DATA | |
FK | COD_CURSO | NUMERO |
Na turma também temos uma questão dependendo do requisito, podemos ter vários valores para as datas, assim teríamos um campo multivalorado e como bem sabe deveríamos então aplicar a 1FN neste campo, isto se nossa regra de negocio pedisse, mas ela não pede então deixaremos, assim como no campo
COD_DEPARTAMENTO
Curso | ||
---|---|---|
PK | COD_CURSO | NUMERO |
NOME_CURSO | CARACTERE | |
FK | COD_DEPARTAMENTO | NUMERO |
Em geral tabelas menores costumam já estar na 1FN
Aluno | ||
---|---|---|
PK | RA | NUMERO |
NOME_ALUNO | CARACTERE | |
SOBRENOME_ALUNO | CARACTERE | |
NOME_RUA | CARACTERE | |
NUMERO_RUA | NUMERO | |
CEP | CARACTERE | |
STATUS | BOOLEAN | |
FILIACAO | CARACTERE | |
SEXO | CARACTERE | |
CONTATO | CARACTERE | |
CPF | CARACTERE | |
TELEFONE | CARCTERE | |
FK | COD_CURSO | NUMERO |
FK | COD_TURMA | NUMERO |
💡 Tabelas maiores tem mais chances de não estarem nas formas normais
Curso_Disciplina | ||
---|---|---|
PK, FK | COD_DISCIPLINA | NUMERO |
PK,FK | COD_CURSO | NUMERO |
Historico | ||
---|---|---|
PK | COD_HISTORICO | NUMERO |
PERIODO_REALIZACAO | DATA | |
FK | RA | NUMERO |
Disciplina | ||
---|---|---|
PK,FK | COD_DISCIPLINA | NUMERO |
NOME_DISCIPLINA | CARACTERE | |
DESCRICAO | CARACTERE | |
NUMERO_ALUNOS | NUMERO | |
CARGA_HORARIA | NUMERO | |
FK | COD_DEPARTAMENTO | NUMERO |
Aluno_Disciplina | ||
---|---|---|
PK,FK | RA | NUMERO |
PK,FK | COD_DISCIPLINA | NUMERO |
Disciplina_Historico | ||
---|---|---|
PK,FK | RA | NUMERO |
PK,FK | COD_DISCIPLINA | NUMERO |
NOTA | NUMERO | |
FREQUENCIA | NUMERO |
Forma 1
🥇 Para estar no 1FN: Tem que ter um atributo único -> cada tabela deve ter sua PK
Não aceitamos fragmentados -> não ter campos multivalorados ou compostos e se tiver é só desmembrar os campos ✍️ É como dizia Jack Estripador: Corte sempre em picadinhos
Para um tabela atingir a primeira forma normal, deve-se seguir as regras:
- EXISTE uma chave primaria
- Somente POSSUI valores atômicos (atributos simples)
- As relações também NÃO possui os atributos multivalorados ou relações aninhadas
- relações aninhadas são tabela dentro de outras tabelas
- Relação NÃO possui atributos compostos
Aplicando
Tabela Aluno
Como na tabela Aluno
temos o campo FILIACAO
que por sua vez é um capo que pode guardar mais de um valor, ou seja, filiação diz de quem você é filho e isso resulta em ter um pai e uma mãe => dois valores (a menos que seja um chiquititas).
- Logo precisamos desmembrar o campo
FILIACAO
-> por ele ser um campo multivalorado - Assim deixaremos a tabela na 1FN
E temos o mesmo na tabela Aluno
no campo CONTATO
que pode receber tanto email, ou whatsapp ou outra forma de contato.
No campo TELEFONE
acontece o mesmo. Já que teremos o telefone residencial e celular
Assim a nova tabela ficara:
Aluno | ||
---|---|---|
PK | RA | NUMERO |
NOME_ALUNO | CARACTERE | |
SOBRENOME_ALUNO | CARACTERE | |
NOME_RUA | CARACTERE | |
NUMERO_RUA | NUMERO | |
CEP | CARACTERE | |
STATUS | BOOLEAN | |
NOME_MAE | CARACTERE | |
NOME_PAI | CARACTERE | |
SEXO | CARACTERE | |
CARACTERE | ||
CARACTERE | ||
CPF | CARACTERE | |
TELEFONE_RESIDENCIAL | CARCTERE | |
TELEFONE_CELULAR | CARCTERE | |
FK | COD_CURSO | NUMERO |
FK | COD_TURMA | NUMERO |
Tabela Historico
Agora se olharmos para a tabela Historico
temos o campo PERIODO_REALIZACAO
que se refere a data de inicio e fim, ou seja, um campo com dois valores.
Assim a nova tabela ficara:
- Tiramos o campo
PERIODO_REALIZACAO
e adicionamos dois campos:- DATA_INICIO
- DATA_FIM
Historico | ||
---|---|---|
PK | COD_HISTORICO | NUMERO |
DATA_INICIO | DATA | |
DATA_FIM | DATA | |
FK | RA | NUMERO |
Forma 2
🥈 Para estar no 2FN tem que: Ser dependente do Spotify PK (Chave Primaria) -> caso contrario está fora
Regras da 2FN:
- Estar na 1FN
- Todos os atributos simples são funcionalmente dependentes de todas as partes da chave primaria
- Não deve existir dependência parcial => ou seja. depende da PK e de outro campo
- Atributos não dependem de chaves candidatas
Caso isso não estiver sendo atingido devemos fazer então uma nova tabela para os dados.
Um atributo-chave é um que é uma PK (Chave Primaria) ou é a parte de uma Chave Composta
Aplicando
Para aplicar devemos nos perguntar primeiro:
Tal campo, depende da PK?
- Caso positivo, então ta okay
- Caso negativo, é cilada bino
Exemplo:
Por exemplo na tabela Professor
:
- Podemos nos perguntar ->
NOME_PROFESSOR
,SOBRENOME_PROFESSOR
eSTATUS
depende deCOD_PROFESSOR
?- Positivo => logo a tabela
Professor
está na 2FN
- Positivo => logo a tabela
Professor | ||
---|---|---|
PK | COD_PROFESSOR | NUMERO |
NOME_PROFESSOR | CARACTERE | |
SOBRENOME_PROFESSOR | CARACTERE | |
STATUS | BOOLEAN | |
FK | COD_DEPARTAMENTO | NUMERO |
Só devemos aplicar essa lógica nos atributos não chave (ou seja. em FK ou PK não aplicamos isso)
Aluno
Os campos Nome da Rua
, Numero da Rua
e CEP
, não pendependem do RA
do Alunos
, eles não variam mesmo se o RA variar.
Se olharmos para Telefone_Residencial
temos um problema, já que se mudar o RA
pode ter a caso do telefone não mudar, então seria melhor colocar em outra tabela;
❗ Lembrando que podemos ou não fazer, isto é, o que estamos fazendo é uma convenção: é melhor fazer mas não é obrigatório
Telefone
Para obedecermos a 2FN iremos criar uma tabela para telefones dos alunos e para o tipo de telefone
Telefone_Aluno | ||
---|---|---|
PK | COD_TELEFONE_ALUNO | NUMERO |
FK | RA | NUMERO |
FK | COD_TIPO_TELEFONE | NUMERO |
NUMERO_TELEFONE | NUMERO | |
Tipo_Telefone | ||
---|---|---|
PK | COD_TIPO_TELEFONE | NUMERO |
TIPO_TELEFONE | CARACTERE |
Endereço
Como os campos que são do endereço ficam de forma parcialmente dependente do Aluno
logo devemos criar uma tabela para o Endereço.
Endereco_Aluno | ||
---|---|---|
PK | COD_ENDERECO_ALUNO | NUMERO |
FK | RA | NUMERO |
FK | COD_TIPO_LOGRADOURO | NUMERO |
CEP | CARACTERE | |
NUMERO_RUA | NUMERO | |
NOME_RUA | CARACTERE | |
COMPLEMENTO | CARACTERE |
Tipo_Logradouro | ||
---|---|---|
PK | COD_TIPO_LOGRADOURO | NUMERO |
TIPO_LOGRADOURO | CARACTERE |
Agora a tabela Aluno ficara assim:
Aluno | ||
---|---|---|
PK | RA | NUMERO |
NOME_ALUNO | CARACTERE | |
SOBRENOME_ALUNO | CARACTERE | |
STATUS | BOOLEAN | |
NOME_MAE | CARACTERE | |
NOME_PAI | CARACTERE | |
SEXO | CARACTERE | |
CARACTERE | ||
CARACTERE | ||
CPF | CARACTERE | |
FK | COD_CURSO | NUMERO |
FK | COD_TURMA | NUMERO |
Forma 3
🥉 Para estar na 3FN tem que: A pessoa deve ser dependente do estado (PK) e não de outra pessoa, ou seja não podemos ter atributos comuns que dependem de outros atributos comuns -> caso contrario está fora; Ou então pense em classes, a classe dos operários devem ser dependentes dos patrões e não podem se juntar -> senão vão para outra tabela
Regras:
- Estar na 2FN
- Não existe dependências transitivas
- Nenhuma coluna não chave depender de outra coluna que não é uma chave
Caso não obedeça devemos criar uma nova tabela.
Como na 2FN, devemos nos perguntar, mas agora a pergunta muda um pouco já que devemos questionar se um atributo simples é dependente de um outro atributo simples, ou seja:
Por exemplo na tabela Professor
:
- Podemos nos perguntar ->
NOME_PROFESSOR
é dependente deSTATUS
?- Vemos que não, já que se o status mudar o nome do professor não muda e vice-versa;
No caso, no exemplo ele já está na terceira forma normal. Então não precisa mexer com mais nada.
Referencias
- https://youtu.be/u6JYYOR_o6E?si=E2MJuCg87PWLMtCF
- https://www.youtube.com/watch?v=-2xK-fkqagI
- https://www.youtube.com/watch?v=XhLmF3-Ew8c
- https://learn.microsoft.com/pt-br/office/troubleshoot/access/database-normalization-description
- https://www.alura.com.br/artigos/normalizacao-banco-de-dados-estrutura?srsltid=AfmBOorWZZuuFq0g6ToGPC2OF-n8TMuQHE0z9QbJ3EXa_qUvSrr554gE
Interessante que para normalizar um banco de dados temos que aumentar o número de tabelas, ao mesmo tempo que temos que ficar fazendo chamadas entre tabelas, o que ativamente diminui a performance do sistema ao fazer querys.
Mas temos a prática da desnormalização onde aumentamos o número de dados redundantes para agilizar as querys.
Isso significa que temos que fazer tabelas mais feias? Não. Existe um método para desnormalizar muito melhor, as views materializadas existentes em bancos de dados como postgresql ou mysql.