Normalização
Ao longo da disciplina, discutimos diferentes modelagens aplicáveis a um mesmo problema. Algumas eram equivalentes entre si, enquanto outras se mostraram mais eficientes. Agora, daremos continuidade à nossa exploração em Banco de Dados, com foco nos conceitos de normalização.
Como vimos no início do curso, bancos de dados relacionais oferecem uma série de benefícios, mas também exigem o cumprimento de boas práticas para que seu uso seja eficaz. A normalização é uma dessas práticas: é um processo que visa aprimorar a modelagem dos dados, reduzindo a redundância e promovendo maior consistência.
O processo de normalização em um banco de dados pode ser conduzido em etapas, nas quais o esquema é avaliado em relação a diferentes propriedades. Cada uma dessas propriedades define uma forma normal. Nem sempre um banco de dados atenderá a todas elas, mas é comum que os bancos sejam modelados considerando até a terceira forma normal (3FN) ou a Forma Normal de Boyce-Codd (FNBC).
Antes de conhecermos as formas normais, precisamos reforçar alguns conceitos importantes:
- Superchave: segundo Elmasri e Navathe (2011), uma superchave é um atributo (ou conjunto de atributos) cujos valores identificam unicamente cada tupla de uma relação. Um detalhe importante é que uma superchave pode conter atributos redundantes. Por exemplo, em uma relação , o conjunto é uma superchave, embora o atributo nome seja redundante, já que por si só é suficiente para identificar exclusivamente cada pessoa. O conceito de chave candidata, por sua vez, elimina esses atributos não essenciais, exigindo minimalidade.
- Atributo principal (ou primo): um atributo principal é aquele que faz parte de alguma chave candidata da relação. Por exemplo, considerando a relação , o atributo é um atributo principal, pois pertence a uma chave candidata (no caso, a chave primária composta da relação). Uma observação importante é que um atributo é considerado principal mesmo que pertença a uma chave candidata que não foi escolhida como chave primária.
- Dependência funcional: em bancos de dados, uma dependência funcional ocorre quando, para quaisquer duas tuplas de uma relação, se elas possuem o mesmo valor para os atributos em X, então necessariamente possuem o mesmo valor para os atributos em Y. Em outras palavras, os valores de X determinam os valores de Y. Por exemplo, considerando a relação
UNIVERSIDADE_FEDERAL(<u>sigla</u>, nome), podemos dizer que existe a dependência funcionalsigla → nome, pois uma mesma sigla, como UFMT, está associada a um único nome, como “Universidade Federal de Mato Grosso”.
- Uma dependência funcional será considerada não trivial quando o(s) atributo(s) determinado(s) não fizer(em) parte do conjunto de atributos determinantes. Ou seja: .
- Dependência funcional transitiva: corre quando existem duas dependências funcionais
X->YeY->Z, de modo queZdepende indiretamente deY. Nesse caso, dizemos que há uma dependência transitivaX->Z, desde queYnão seja uma superchave. Por exemplo, considerando a relaçãoCAMPUS(universidade_federal_sigla, estado, regiao), temos:
universidade_federal_sigla → estadoestado → regiao
Assim, regiao depende transitivamente de universidade_federal_sigla.
Agora que já reforçamos os conceitos, vamos às definições.
Primeira forma normal (1FN)
Uma relação está na Primeira Forma Normal (1FN) se, e somente se, todos os valores de seus atributos forem atômicos e pertencentes aos respectivos domínios. Além disso, cada tupla deve ser identificável univocamente por uma chave primária.
De forma simples e direta, os valores em cada coluna (atributo) devem ser indivisíveis. Isso impede que uma única célula contenha múltiplos valores (como uma lista de telefones) ou valores estruturados e não atômicos (como um endereço completo com rua, número e CEP em um único campo). Vejamos um exemplo:
PROFESSOR
| 🔑 siape (PK) | nome | emails |
|---|---|---|
| 123456 | José da Silva Sauro | jose@universidade.br, jose.prof@provedor.com |
| 934352 | Maria Santos | maria@universidade.br |
Note que a relação PROFESSOR viola a primeira forma normal, já que o atributo emails é multivalorado. Para tornar essa relação compatível com a 1FN, transformamos esse atributo numa relação EMAIL. A cardinalidade do relacionamento entre PROFESSOR e EMAIL tende a ser 1:N (um professor pode ter vários e-mails). Naturalmente, se a regra de negócio permitisse que um endereço de e-mail fosse usado por mais de um professor, o relacionamento teria cardinalidade N:M (neste caso, precisaríamos de uma tabela associativa).
Após a transformação, considerando a cardinalidade 1:N, o resultado seria:
PROFESSOR
| 🔑 siape (PK) | nome |
|---|---|
| 123456 | José da Silva Sauro |
| 934352 | Maria Santos |
| 🔑 email (PK) | professor_siape (FK) |
|---|---|
| jose@universidade.br | 123456 |
| jose.prof@provedor.com | 123456 |
| maria@universidade.br | 934352 |
No caso de atributos compostos como Endereço, a adequação à 1FN pode ser feita por meio da decomposição em atributos atômicos (rua, número, cidade etc.), sem necessidade de criar uma nova relação. Entretanto, caso o endereço seja multivalorado (por exemplo, um professor possuindo vários endereços), será necessário modelá-lo como uma nova relação, resultando em um relacionamento do tipo 1:N.
Segunda forma normal (2FN)
Definição simples
Para a aplicação da 2FN, devemos cumprir um requisito importante: a relação já deve estar na 1FN. Depois, verificamos a seguinte condição específica:
Uma relação está na segunda forma normal (2FN) se, e somente se, os atributos não-principais de tiverem dependência funcional total de todos os atributos que formam a chave primária de .
Um atributo não-principal é aquele que não faz parte de nenhuma chave candidata. Em outras palavras, ele é um atributo que não é essencial para identificar, de forma única, uma tupla na relação.
Em português claro, o que essa definição quer dizer é: quando uma relação tem sua chave primária composta, ou seja, formada por mais de um atributo, todos os atributos que não são chave devem ser funcionalmente dependentes de toda a chave primária, e não de apenas parte dela. Se uma relação possui apenas uma chaves primária simples (não composta), então ela não pode apresentar dependências parciais e, portanto, satisfaz automaticamente a 2FN (de acordo com essa definição inicial).
Vejamos o exemplo a seguir, que consiste numa relação que representa a nota alcançada por um estudante em uma disciplina. A chave primária desta relação é composta pelos atributos RGA e CodOfertaDisciplina.
| 🔑 RGA | 🔑 CodOfertaDisciplina | Nome_Aluno | Nome_Disciplina | Nota |
|---|---|---|---|---|
| 20231001 | DSI101 | Ana Silva | Banco de Dados | 7,5 |
| 20231001 | MAT201 | Ana Silva | Cálculo | 9,0 |
| 20231002 | DSI101 | Francisco Costa | Banco de Dados | 5,0 |
Veja que, nesta relação, o atributo Nota depende da chave primária completa (a identificação do aluno e a identificação da disciplina).
Já o atributo Nome_Aluno depende apenas de RGA, que é parte da chave primária composta (RGA, CodOfertaDisciplina). Isso caracteriza uma dependência funcional parcial, que viola a 2FN. De forma semelhante, o atributo Nome_Disciplina depende apenas de CodOfertaDisciplina, sem qualquer dependência de RGA, também causando dependência parcial.
Para que a relação esteja na 2FN, esses atributos deveriam ser removidos para relações separadas em que a chave primária seja o atributo do qual eles dependem totalmente. Por exemplo, Nome_Aluno poderia estar numa relação com chave RGA, enquanto Nome_Disciplina estaria numa relação com chave CodOfertaDisciplina.
Veja como ficaria o resultado:
OFERTA_DISCIPLINA
| 🔑CodOfertaDisciplina (PK) | Nome_Disciplina |
|---|---|
| DSI101 | Banco de Dados |
| MAT201 | Cálculo |
ALUNO
| 🔑 RGA (PK) | Nome_Aluno |
|---|---|
| 20231001 | Ana Silva |
| 20231002 | Francisco Costa |
ALUNO_DISCIPLINA
| 🔑 RGA (PK, FK) | 🔑 CodOfertaDisciplina (PK, FK) | Nota |
|---|---|---|
| 20231001 | DSI101 | 7,5 |
| 20231001 | MAT201 | 9,0 |
| 20231002 | DSI101 | 5,0 |
A relação ALUNO_DISCIPLINA funciona como uma tabela associativa que liga os alunos às disciplinas das quais estão matriculados, contendo as chaves primárias de ALUNO e OFERTA_DISCIPLINA.
Definição restritiva
Elmasri e Navathe também apresentam outra versão mais restritiva da 2FN: nela, em vez de se analisar apenas a chave primária, considera-se qualquer atributo chave.
Uma relação está na 2FN se cada atributo não principal for total e funcionalmente dependente de cada chave de .
Ou seja: agora, olhamos não apenas para a dependência total da chave primária, mas de todos os atributos que são únicos para as tuplas daquela relação.
Para podermos visualizar essa situação, vamos considerar a relação anterior, com uma pequena diferença: criaremos uma chave substituta (surrogate key) para que a relação tenha uma chave primária simples.
| 🔑 idMatricula | RGA | CodOfertaDisciplina | Nome_Aluno | Nome_Disciplina | Nota |
|---|---|---|---|---|---|
| 1 | 20231001 | DSI101 | Ana Silva | Banco de Dados | 7,5 |
| 2 | 20231001 | MAT201 | Ana Silva | Cálculo | 9,0 |
| 3 | 20231002 | DSI101 | Francisco Costa | Banco de Dados | 5,0 |
Veja que agora a chave primária é simples (formada por um único atributo). Se fôssemos considerar apenas a primeira definição formal da 2FN, nem precisaráimos nos preocupar com essa relação. No entanto, com a nova definição, chegamos a um ponto importante: a combinação de é chave candidata para a relação. Logo, temos que garantir que todos os atributos dependam não apenas de , mas também da combinação de .
Terceira Forma Normal (3FN)
Definição simples
Após garantir que uma relação está na 2FN, podemos verificar se ela também atende à terceira forma normal. A 3FN busca eliminar outro tipo de dependência: a dependência transitiva. Sua definição original é:
Uma relação está na 3FN se, além de satisfazer a 2FN, não tem nenhum atributo não-principal transitivamente dependente da chave primária.
Como vimos no início desta página, a dependência transitiva ocorre quando um atributo não-principal depende funcionalmente de outro atributo não-principal, que por sua vez depende da chave primária. Isso significa que existe um “caminho indireto” de dependência entre um atributo não-principal e a chave, causando redundância e possíveis anomalias na relação.
Vejamos o seguinte exemplo:
DISCIPLINA
| 🔑 CodDscp | NomeDscp | CodDepto | NomeDepto |
|---|---|---|---|
| 1 | Redes de Computadores | 002 | DCA |
| 2 | Programação Orientada a Objetos | 002 | DCA |
| 3 | Introdução à Mineração | 001 | DTQM |
| 4 | Tópicos em Engenharia Química | 001 | DTQM |
Note que, neste caso, assumindo que cada disciplina pertence a um único departamento, há uma dependência funcional: CodDscp -> CodDepto, já que o departamento é determinado pelo código da disciplina. No entanto, há uma dependência funcional CodDepto -> NomeDepto, em que CodDepto não é uma superchave.
Para corrigir, dividimos em duas relações:
DEPARTAMENTO
| 🔑 CodDepto | NomeDepto |
|---|---|
| 001 | DTQM |
| 002 | DCA |
DISCIPLINA
| 🔑 CodDscp | NomeDscp | CodDepto (FK) |
|---|---|---|
| 1 | Redes de Computadores | 002 |
| 2 | Programação Orientada a Objetos | 002 |
| 3 | Introdução à Mineração | 001 |
| 4 | Tópicos em Engenharia Química | 001 |
Definição restritiva
Há, também, uma definição mais restritiva para a 3FN, que estabelece:
Uma relação está na 3FN se, além de satisfazer a 2FN, para toda dependência funcional não trivial , pelo menos uma das condições é satisfeita:
- é superchave; ou
- é atributo principal.