Antes de mais nada, não vou começar falando de SQL como todo mundo por ai faz [ copiando trechos da Wikipedia ]. Se você está lendo meu blog, então vc sabe o que é SQL, sabe para que serve, e até provavelmente já trabalha com essa linguagem. Okay, vamos prosseguir.

Um conceito importante e interessante, mas que é fatalmente sonegado aos programadores, é o conceito de entidade [Basta tentar pesquisar por este termo, e notar como é escasso o conteúdo].

O resultado, são sistemas mal modelados, não normalizados, em que a aplicação tenta resolver diversos problemas da estrutura. Onde qualquer novo requisito, gera um grande transtorno, pois a análise para criação do modelo, não foi feita corretamente, de modo a deixar a estrutura robusta e expansível.

Um exercício rápido:

Volante, Pára-choque, Retrovisores, Câmbio, Chassis, Rodas, Motor, Portas…

E ai, o que lhe veio a mente ? Qual objeto do nosso mundo, agrega essas partes que citei ?

Espero que a sua resposta seja ‘um carro’. Faz parte do senso comum.

Mais uma vez:

Galhos, Folhas, Raíz, Tronco, Copa, Calotas..

Uma árvore certo ? só as ‘Calotas’ que parecem não estar no lugar correto, pois não combinam com os outros itens, e não conseguimos imaginar que as calotas, devam pertencer a nossa árvore.

Melhor mover as Calotas para o Carro ali de cima, no senso comum, para se encaixar melhor em uma normalidade aceitável.

Ao iniciar o levantamento de informações, o processo é mais ou menos parecido com esse.

O Carro, e a Árvore são as nossas entidades e cada um dos itens que citei, são os atributos particulares dessas entidades.

A idéia é saber a quem deve pertencer cada atributo, e sermos capazes de definirmos as entidades do nosso sistema.

Formalmente:

Uma entidade possui atributos.
Os atributos são as características, e não devem conter um grupo de informações.
Não existem entidades com menos de 2 atributos, logo, cada entidade, é em si, um grupo de atributos.

Talvez esteja confuso até aqui, mas trazendo de forma livre para a nossa realidade, cada Entidade é uma tabela, e cada Atributo é cada uma das colunas dessas tabelas.

O ponto que quero levantar, é a compreensão do conceito.

Se tenho que fazer um cadastro de usuário, preciso pensar o que a entidade usuário significa para o meu sistema, e o que preciso saber dela ou não.

CREATE TABLE `test`.`usuario` (
   `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
   `nome` VARCHAR( 50 ) NOT NULL
) ENGINE = InnoDB;

Até aqui bem simples. A nossa entidade usuario possui um `nome`, e um `id`, que é um identificador único desse objeto no nosso modelo.

Agora o cliente nos informa que ele precisa saber dos usuarios dele, pelo menos:

-> um telefone residencial, e

-> um celular.

Num primeiro momento, a nossa reação é adicionarmos essas informações à entidade usuario, pois o telefone e o celular pertencem respectivamente, a cada usuário nosso.

ALTER TABLE `usuario` ADD `telefone` VARCHAR( 14 ) NOT NULL ,
ADD `celular` VARCHAR( 14 ) NOT NULL 

Porém, nosso chefe, vem e nos informa que agora ele precisa também do Telefone Comercial do nosso usuário, e quem sabe talvez um Telefone de Contato.. é nesse momento, que o nosso modelo pode naufragar ou não.

Adicionar mais essas 2 colunas a tabela, seria um erro, no ponto de vista da modelagem de negócios, pois estaríamos novamente, subestimando a aplicação. E se lá pra frente, o cara resolver incluir o Pager (ainda usam isso? hahauhaua), ou Nextel do camarada ?

Nesta hora, deveria começar a ficar claro que essa expansão, clama por uma nova entidade na nossa modelagem.

ALTER TABLE `usuario` DROP `telefone` , DROP `celular` ;

Estrutura da nova entidade:

Dados cadastrados:

Portanto, se precisamos de mais formas de contato, como o Nextel, basta adicionarmos um registro a essa tabela, e isso pode ser facilmente feito na nossa aplicação, sem a necessidade de alterarmos a estrutura do modelo.

INSERT INTO `test`.`contato` (`id`, `nome`) VALUES (NULL, 'Nextel');

A única questão não respondida ainda, é como vincular os dados do usuário a esta nossa nova entidade. Fazemos então, outra tabela, já bem normalizada, da seguinte forma:

CREATE TABLE `test`.`contato_usuario` (
    `id_usuario` INT NOT NULL ,
    `id_contato` INT NOT NULL ,
    `descricao` VARCHAR( 70 ) NOT NULL
) ENGINE = InnoDB;

Essa nossa nova tabela, será responsável por fazer a ligação entre as nossas entidades.

A relação entre as tabelas é evidente, e o nosso JOIN, para resgatar esses dados fica:

SELECT `usuario`.`id` AS `id_usuario` , `usuario`.`nome` AS `nome_usuario` ,
  `contato`.`nome` AS `nome_contato` , `contato_usuario`.`descricao`
    FROM `usuario`
        INNER JOIN `contato_usuario` ON `usuario`.`id` = `contato_usuario`.`id_usuario`
        INNER JOIN `contato` ON `contato`.`id` = `contato_usuario`.`id_contato`

Saída:

Continuando, surgem novos atributos:

data de nascimento, cidade natal, email, estado civil..

Precisamos dividir esses atributos nas nossas entidades.

data de nascimento, cidade natal e estado civil, cabem muito bem na entidade usuario.

Já o email, podemos colocar na entidade contato, concorda ?

Se lá pra frente, precisarmos de um ‘email_secundario’, para proteção do sistema, onde o usuário poderá resgatar a senha dele na nossa aplicação, bastará novamente, inserirmos um registro na entidade contato, sem a menor alteração do modelo que criamos.

Esse particionamento que fizemos tem o seu custo. Maior complexidade do modelo, queda de performance(afinal, agora consultamos 3 tabelas, e não apenas uma), em contrapartida com os ganhos que essa modelagem trouxe a estrutura da nossa aplicação.

Analise bem os requisitos do seu sistema antes de implementá-lo. Não tome todo e qualquer tutorial como lei, ou verdade absoluta.

Dump completo do SQL usado:

-- phpMyAdmin SQL Dump
-- version 3.3.8.1
-- http://www.phpmyadmin.net
--
-- Servidor: localhost
-- Tempo de Geração: Mar 29, 2011 as 02:54 AM
-- Versão do Servidor: 5.5.8
-- Versão do PHP: 5.3.4

SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;

--
-- Banco de Dados: `test`
--

-- --------------------------------------------------------

--
-- Estrutura da tabela `contato`
--

CREATE TABLE IF NOT EXISTS `contato` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `nome` varchar(40) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;

--
-- Extraindo dados da tabela `contato`
--

INSERT INTO `contato` (`id`, `nome`) VALUES
(1, 'Telefone Residencial'),
(2, 'Telefone Comercial'),
(3, 'Celular'),
(4, 'Nextel');

-- --------------------------------------------------------

--
-- Estrutura da tabela `contato_usuario`
--

CREATE TABLE IF NOT EXISTS `contato_usuario` (
  `id_usuario` int(11) NOT NULL,
  `id_contato` int(11) NOT NULL,
  `descricao` varchar(70) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- Extraindo dados da tabela `contato_usuario`
--

INSERT INTO `contato_usuario` (`id_usuario`, `id_contato`, `descricao`) VALUES
(1, 2, '(11) 1234-5678'),
(1, 4, '55*11*1111'),
(2, 1, '(12) 4321-8765'),
(2, 2, '(11) 8765-4321'),
(2, 4, '55*22*2222');

-- --------------------------------------------------------

--
-- Estrutura da tabela `usuario`
--

CREATE TABLE IF NOT EXISTS `usuario` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `nome` varchar(50) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;

--
-- Extraindo dados da tabela `usuario`
--

INSERT INTO `usuario` (`id`, `nome`) VALUES
(1, 'Bruno'),
(2, 'Silveira');