Normalização

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:

  1. 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 PESSOA(cpf,nome)PESSOA(\underline{cpf}, nome), o conjunto cpf,nome\underline{cpf}, nome é uma superchave, embora o atributo nome seja redundante, já que cpf\underline{cpf} 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.
  2. Atributo principal (ou primo): um atributo principal é aquele que faz parte de alguma chave candidata da relação. Por exemplo, considerando a relação PESSOA(rgNumero,rgUF,rgOrgao,nome)PESSOA(\underline{rgNumero, rgUF, rgOrgao}, nome), o atributo rgOrgaorgOrgao é 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.
  3. Dependência funcional: em bancos de dados, uma dependência funcional XYX → Y 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 funcional sigla → nome, pois uma mesma sigla, como UFMT, está associada a um único nome, como “Universidade Federal de Mato Grosso”.
  • Uma dependência funcional (X>Y)(X->Y) será considerada não trivial quando o(s) atributo(s) determinado(s) não fizer(em) parte do conjunto de atributos determinantes. Ou seja: (Y⊈X)(Y \not\subseteq X).
  1. Dependência funcional transitiva: corre quando existem duas dependências funcionais X->Y e Y->Z, de modo que Z depende indiretamente de Y. Nesse caso, dizemos que há uma dependência transitiva X->Z, desde que Y não seja uma superchave. Por exemplo, considerando a relação CAMPUS(universidade_federal_sigla, estado, regiao), temos:
  • universidade_federal_sigla → estado
  • estado → 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)nomeemails
123456José da Silva Saurojose@universidade.br, jose.prof@provedor.com
934352Maria Santosmaria@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
123456José da Silva Sauro
934352Maria Santos

EMAIL

🔑 email (PK)professor_siape (FK)
jose@universidade.br123456
jose.prof@provedor.com123456
maria@universidade.br934352

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 RR está na segunda forma normal (2FN) se, e somente se, os atributos não-principais de RR tiverem dependência funcional total de todos os atributos que formam a chave primária de RR.

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 RR 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🔑 CodOfertaDisciplinaNome_AlunoNome_DisciplinaNota
20231001DSI101Ana SilvaBanco de Dados7,5
20231001MAT201Ana SilvaCálculo9,0
20231002DSI101Francisco CostaBanco de Dados5,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
DSI101Banco de Dados
MAT201Cálculo

ALUNO

🔑 RGA (PK)Nome_Aluno
20231001Ana Silva
20231002Francisco Costa

ALUNO_DISCIPLINA

🔑 RGA (PK, FK)🔑 CodOfertaDisciplina (PK, FK)Nota
20231001DSI1017,5
20231001MAT2019,0
20231002DSI1015,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 RR está na 2FN se cada atributo não principal AA for total e funcionalmente dependente de cada chave de RR.

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.

🔑 idMatriculaRGACodOfertaDisciplinaNome_AlunoNome_DisciplinaNota
120231001DSI101Ana SilvaBanco de Dados7,5
220231001MAT201Ana SilvaCálculo9,0
320231002DSI101Francisco CostaBanco de Dados5,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 RGA,CodOfertaDisciplinaRGA,CodOfertaDisciplina é chave candidata para a relação. Logo, temos que garantir que todos os atributos dependam não apenas de idMatriculaidMatricula, mas também da combinação de RGA,CodOfertaDisciplinaRGA,CodOfertaDisciplina.

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 RR 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

🔑 CodDscpNomeDscpCodDeptoNomeDepto
1Redes de Computadores002DCA
2Programação Orientada a Objetos002DCA
3Introdução à Mineração001DTQM
4Tópicos em Engenharia Química001DTQM

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

🔑 CodDeptoNomeDepto
001DTQM
002DCA

DISCIPLINA

🔑 CodDscpNomeDscpCodDepto (FK)
1Redes de Computadores002
2Programação Orientada a Objetos002
3Introdução à Mineração001
4Tópicos em Engenharia Química001

Definição restritiva

Há, também, uma definição mais restritiva para a 3FN, que estabelece:

Uma relação RR está na 3FN se, além de satisfazer a 2FN, para toda dependência funcional não trivial XYX \rightarrow Y, pelo menos uma das condições é satisfeita:

  • XX é superchave; ou
  • YY é atributo principal.