Normalização de Banco de Dados

A aplicação da normalização pode evitar alguns  problemas no banco de dados, tais como:

  • grupos repetitivos de dados;
  • redundâncias de dados desnecessárias;
  • perdas acidentais de informação;
  • dificuldade na representação de fatos da realidade observada;

A normalização de banco de dados  é uma técnica que permite evitar  inconsistências no banco como por exemplo informações com duplicidade, dependências funcionais mal resolvidas, etc.

(1FN) Primeira Forma Normal: tabelas sem grupos de repetição.

1º Problema: redundância e inconsistência

2º Problema: aumento desnecessário do volume de dados.

Exemplo: Tem-se uma tabela cliente no qual temos os campos id, nome e cidade.

cli

Pode-se perceber que existem repetições nas cidades, a cidade de São Paulo por exemplo está escrito de duas formas diferentes, mas fazem referência à mesma cidade, o mesmo acontece com a cidade de Belo Horizonte, ou seja, numa pesquisa por clientes da cidade de São Paulo por exemplo, tem-se como resultado neste caso apenas um cliente, quando na verdade existem dois cliente desta cidade.

Pode-se resolver isto da seguinte maneira:

Criamos uma tabela cidade e a tabela cliente tera uma chave estrangeira da tabela cidade:

cli1

Tabela cidade:

cid1

Neste caso, ainda fizemos uma tabela para o estado usando a mesma norma:

uf


OBS: Para que na tabela cidade não exsita duas cidades com o mesmo nome em um mesmo estado, é necessário que se faça uma Unique Key para o nome e o id_uf

(2FN) Segunda Forma Normal: Quando PK composta, cada atributo que não for PK deve depender de todas as chaves:

Por exemplo uma tabela pedido, cujo a chave  primária composta  é formado pelo id do cliente e o id do produto:

pedido

Veja que temos também uma atributo cor, porém este campo faz referência apenas ao produto, então de acordo com norma a tabela mostrada acima está incorreta, o atributo cor deve estar na tabela produto e não na tabela pedido:

produto

(3FN) Terceira Forma Normal: Todos os campos que não forem PK devem sempre depender da chave primária, é o mesmo que 2FN, porém esta faz referencia à chave primária simples.

Por exemplo uma tabela funcionário:

func

Na tabela acima nos temos o id do funcionário, o nome do funcionário, a sua categoria e o piso salarial da categoria do funcionário, perceba que o piso salarial faz referência a categoria e não ao funcionário, então de acordo com a 3FN o correto seria o seguinte:

catt

func2

Fazemos uma tabela categoria e nesta sim colocamos o piso salarial, pois este faz referência à categoria, e na tabela funcionário coloca-se uma chave estrangeira referenciando a tabela categoria!

Referência: Aula de Banco de Dados – Univás – Profº: Artur

SQL básico, uma pequena pincelada!!!

Abaixo segue uma explicação resumida e rápida sobre SQL e algumas de suas instruções como SELECT, FROM, WHERE, JOIN, etc. Como já tive várias dúvidas com elas, imagino que outras pessoas possam ter também!

Para ilustração vamos imaginar um banco de dados de uma fornecedora de um produto qualquer, peguemos então duas tabelas: a tabela de produtos e a tabela de pedidos:

tab_produto


Antes de mais nada vamos citar alguns conceitos e termos  importantes:

PRIMARY KEY ou CHAVE PRIMÁRIA: identifica o registro (tupla)  univocamente, ou seja cada linha terá sua identificação que não poderá se repetir. Nas tabelas acima por exemplo a primary key das tabelas produto e pedido são os campos id, podemos perceber que os valores da coluna id não se repetem.

FOREIGN_KEY ou CHAVE ESTRANGEIRA: faz referência à um atributo de outra tabela, por exemplo na tabela pedido acima existe um campo chamado produto_id que faz referência ao campo id da tabela produto.

Vamos começar então com o código abaixo:

SELECT *

FROM produto, pedido

O resultado da consulta acima será o produto cartesiano  das duas tabelas, ou seja teremos uma tabela resultado de 12 registros, 3 registros de produto X 4 registros de pedidos, desta forma vamos ter registros sem lógica, pois apenas fizemos o produto cartesiano das duas tabelas, nada a mais.

produto_cart

Assim, o resultado será o ilustrado abaixo, podemos ver que como estamos usando asterísco (*) na cláusula SELECT, irá aparecer todos os campos das duas tabelas, pois não especificamos nenhum, o asterísco quer dizer TODOS os campos, podemos perceber tambem a falta de lógica dos registro como por exemplo o produto com id 2 junto com o pedido de id 1 que pertence ao produdo de id 1, isso acontece porque o que foi feito foi apenas um produto cartesiano como no esquema ilustrado acima :

Agora vamos fazer uma primeira filtragem, para que possamos ter apenas registros que tenham lógica entre si:

SELECT *

FROM produto AS pr,

pedido AS pd

WHERE pr.id = pd.produto_id

Incluimos agora a instrução WHERE que está fazendo um primeiro filtro, ou seja só está nos mostrando os registro cujo o id da tabela produto seja igual ao id_produto da tabela pedido a cláusula AS indica que daremos um pseudônimo à nossa tabela, desta forma podemos trabalhar com nomes menores. Agora sim podemos ver registros que tenham lógica:

primeiro_filtro

Podemos também fazer a consulta acima usando a instrução JOIN que fará uma combinação das tabelas produto e pedido e depois uma interseção entre as duas tabelas, o resultado será o mesmo do de cima, porém usando o JOIN ganharemos muito mais performance na consulta o que faz muita diferença quando trabalhamos com volumes grandes de registros.

SELECT *

FROM produto AS pr

JOIN pedido AS pd

ON pr.id = pd.produto_id

Abaixo segue ilustração de como podemos exemplicar a consulta usando conjuntos, substituindo os conjuntos pela nossas tabelas fica bastante fácil de se entender!

conj21


Agora além de fazermos a interseção das tabelas vamos também fazer uma filtragem que só  retorne os registros que contenham no pedido o produto caneta:

SELECT *

FROM produto AS pr

JOIN pedido AS pd

ON pr.id = pd.produto_id

WHERE pr.nome = ‘CANETA’


No código acima estamos selecionando (SELECT) todos os campos (colunas) das tabelas produto e pedido,  cujo o id chave primária (PRIMARY KEY) da tabela produto seja igual ao pedido_id que é uma chave estrangeira ( FOREIGN_KEY) na tabela pedido, e ainda que o nome do produto seja “CANETA”.

Neste caso resultado desta consulta seria o seguinte:

result

Poderiamos ter feito essa mesma consulta da seguinte maneira:

SELECT *

FROM produto AS pr,

pedido AS pd

WHERE pr.id = pd.produto_id AND pr.nome = ‘CANETA’

Desta maneira ele também faria a filtragem assim como o join, porém ele perderia bastante na performance da consulta.

Então desta maneira podemos perceber que no SELECT colocamos os campos que queremos que apareça na consulta (colunas), no caso apresentado acima nós usamos um asterísco (*) então queremos que apareça todos os campos das duas tabelas. No FROM serão colocadas as tableas que queremos consultar, e com o WHERE fazemos as filtragens que forem necessárias.

FUNÇÕES DE AGREGAÇÃO:

COUNT: conta o número de registros do campo passado como parâmetro da consulta.

EX: Contar quantos produtos existem cadastrados na tabela produto.

SELECT COUNT(id) AS qtd_produto

FROM produto;

Resultado:

count

AVG: média do valor do conjunto do campo passado como parâmetro na consulta.

EX: Fazer a média da quantidade de produto de todos os pedidos feitos.

SELECT AVG(quantidade) AS media_qtd

FROM pedido;

Resultado:

avg

SUM: soma dos valores do conjunto do campo passado como parâmetro.

EX: Saber o total de produtos pedidos.

SELECT SUM(quantidade) AS TOTAL

FROM pedido;

Resultado:

sum

MIN: menor valor do conjunto do campo passado como parâmetro.

EX: O pedido com menor quantidade de produto.

SELECT MIN(quantidade) AS menor_valor

FROM pedido;

Resultado:

min

MAX: maior valor do conjunto do campo passado como parâmetro.

EX: O pedido com maior quantidade de produto.

SELECT MAX(quantidade) AS maior_valor

FROM pedido;

Resultado:

max

Agora iremos criar uma nova tabela, a tabela cliente que terá duas colunas o seu id e nome do cliente:

cliente

E na tabela pedido vamos adicionar mais uma coluna que será uma chave estrangeira (FOREIGN KEY) que fará referência ao id do cliente. Referenciamos todos os cliente da tabela exceto o cliente de nome Paulo, vamos supor que ele ainda não tenha feito nenhum pedido. E também  adicionamos mais um pedido do cliente de id 1 (João).

ta2

Vamos imaginar que queremos saber o nome de todos os clientes que fizeram pedido do produto de id 1:

SELECT c.nome

FROM cliente AS c

JOIN pedido AS p

ON c.id = p.cliente_id

WHERE p.produto_id = 1;

O resultado desta consulta será o ilustrado abaixo, podemos perceber que o cliente João aparece duas vezes porque ele fez dois pedidos do produto 1, uma vez com a quantidade de 100 e outra com a quantidade de 300. Porém não precisamos que o nome dele aparece repetidas vezes, senão se um cliente fazer o pedido do mesmo produto 1000 vezes, aparecerá 1000 vezes o nome dele repedido na consulta.

sem_sest

Para resolvermos isto usamos a instrução DISTINCT, ele fará com que não se repita os resultado da consulta:

SELECT DISTINCT(c.nome)

FROM cliente AS c

JOIN pedido AS p

ON c.id = p.cliente_id

WHERE p.produto_id = 1;

Está será a tabela resultado, sem repetições:

dist

Agora supomos que desejamos saber quantos pedidos cada cliente fez, para isso vamos usar a cláusula GROUP BY, que faz o agrupamento de algum paramêtro.

SELECT c.nome, COUNT(p.id) AS Quantidade

FROM cliente AS c

JOIN pedido AS p

ON c.id=p.cliente_id

GROUP BY c.nome;

Acima estamos selecionando o nome do cliente e com a função COUNT estamos contando os pedidos de cada cliente e com o GROUP BY agrupamos por nome do cliente a quantidade de pedidos. Então para cada cliente referenciado na tabela pedido será contado cada pedido que ele fez. A tabela resultado da consulta acima é essa:

group

SELECT ANINHADO

o selcet aninhado é uma subconsulta, é um comando SELECT embutido em uma outra cláusula SQL.

Imaginemos que gostariamos consultar todos os dados dos clientes que não fizeram nenhum pedido:

SELECT c.*

FROM cliente as c

WHERE c.id NOT IN (

SELECT DISTINCT(c.id)

FROM cliente as c

JOIN pedido as p

ON c.id = p.cliente_id)

Na consulta acima pedimos para que seja mostrados todos os campo da tabela cliente onde o id do cliente não existisse na consulta do segundo SELECT,  ou seja, para selicionar todos os atributos da tabela cliente que não existisse na tabela pedido. O resultado seria este:

aninhado

Apenas o cliente Paulo não fez nenhum pedido ainda!

Referência: Aula de Banco de Dados – Profº Artur – Univás.