Relacionamento de Tabelas no MySQL

Hoje vamos a mais um tutorial “step by step” onde vou falar e mostrar como fazer relacionamento entre tabelas no MySQL.

O relacionamento de tabelas é necessário quando temos mais de uma tabela com informações que podem e precisam ser cruzadas, por exemplo: categorias e produtos… Cada registro na tabela produtos estará ligado a um registro da tabela categorias.

Só pra vocês saberem, existem três níveis de relacionamento: nosso exemplo será um relação de 1:N (fala-se “um pra N” ou “um para muitos”) onde cada categoria (1) contém um ou mais produtos (N)… Há também o 1:1 onde cada registro de uma tabela (1) está ligado a um e somente um registro de outra tabela (1)… E há outro nível de relacionamento, mais complexo, que é o N:N onde um ou mais registros de uma tabela (N) estão relacionados a um ou mais registros de outra tabela (N), que seria o exemplo de duas tabelas “produtos” e “tags” onde um produto tem várias tags e vários produtos pertencem a uma tag.

Não vou me aprofundar muito no assunto… Vou falar apenas da relação mais comum (1:N) e dar exemplos de como trabalhar com elas.

Para o nosso exemplo de hoje usaremos duas tabelas, chamadas “categorias” e “produtos”:

01.CREATE TABLE `categorias` (
02.    `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
03.    `nome` VARCHAR( 255 ) NOT NULL
04.) ENGINE = MYISAM;
05.  
06.CREATE TABLE `produtos` (
07.    `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
08.    `categoria_id` INT NOT NULL ,
09.    `nome` VARCHAR( 255 ) NOT NULL ,
10.    `preco` DECIMAL( 10,2 ) NOT NULL
11.) ENGINE = MYISAM;

E vamos inserir alguns dados para exemplo:

1.-- Extraindo dados da tabela `categorias`
2.INSERT INTO `categorias` VALUES(1, 'Camisetas');
3.INSERT INTO `categorias` VALUES(2, 'Canecas');
4.  
5.-- Extraindo dados da tabela `produtos`
6.INSERT INTO `produtos` VALUES(1, 1, 'Camiseta Social', 15.00);
7.INSERT INTO `produtos` VALUES(2, 1, 'Camiseta Regata', 11.99);
8.INSERT INTO `produtos` VALUES(3, 2, 'Caneca Grande', 12.00);

Reparem que na tabela produtos temos uma coluna “especial”, que é a “categoria_id” (INT)… Ela é quem ajudará a fazer a relação das duas tabelas… Nessa coluna entrará o ID da categoria a qual o produto pertence… Ou seja: as duas camisetas pertencem a categoria “Camisetas” (ID 1) e o terceiro produto (a Caneca Grande) pertence a categoria “Canecas” (ID 2) e é na coluna “categoria_id” que armazenamos esses IDs que identificam as categorias.

Esse campo responsável pela relação é normalmente chamado de “foreing key” (fk) ou “chave estrangeira”.

Mas qual a utilidade dessa tal “relação”?

Sem usar o relacionamento você poderia pegar todos os produtos e depois pegar as informações das categorias com uma segunda consulta, assim:

01.<?php
02.  
03.// Consulta que pega todos os produtos
04.$sql = "SELECT * FROM `produtos` ORDER BY `nome` ASC";
05.$query = mysql_query($sql);
06.while ($produto = mysql_fetch_assoc($query)) {
07.    // Aqui temos o array $produto com todos os valores do produto
08.  
09.    // Consulta para pegar os dados da categoria:
10.    $sqlC = "SELECT * FROM `categorias` WHERE `id` = " . $produto['categoria_id'];
11.    $queryC = mysql_query($sqlC);
12.    $categoria = mysql_fetch_assoc($queryC);
13.  
14.    echo 'Titulo: ' . $produto['nome'] . '<br />';
15.    echo 'Preço: ' . $produto['preco'] . '<br />';
16.    echo 'Categoria: ' . $categoria['nome']. '<br />';
17.    echo '<hr />';
18.}
19.  
20.?>

Até aí tudo bem… Não tem nenhum pecado nisso… Mas imagine que você tem uma loja com 1000 produtos (o que não é muito), seria executada 1 consulta para todos os produtos e, dentro do loop (while) seriam executadas outras 1000 consultas para pegar o nome da categoria a qual o produto pertence… Ou seja, 1001 consultas, o que é um absurdo.

A mágica da relação

Agora vamos montar uma consulta que DE UMA SÓ VEZ irá pegar os dados de cada produto e também o nome da categoria… Com isso reduziremos nossas 1001 consultas pra… uma só! Sem mistérios, sem sub-consultas, nem consultas dentro do while()! :D

Mas antes de mostrar o script vou ajudar a vocês entenderem como a relação é feita… Antes a nossa consulta que pega apenas os produtos era assim:

1.SELECT * FROM `produtos` ORDER BY `nome` ASC

Sua tradução seria: SELECIONAR todas as colunas da TABELA `produtos` ORDENADO PELO `nome` ASCENDETEMENTE

 

Agora usaremos uma nova “palavra” do MySQL que é o JOIN (tradução: “unir”) e serve para unir resultados de duas tabelas.. ;)

Existem três tipos de JOIN mas não vou falar dos outros dois pois eles são MUITO pouco usados… Falaremos do “INNER JOIN” que exige que haja um registro que corresponda a relação nas duas tabelas, ou seja: se houver um produto sem categoria ou a categoria não existir na tabela categorias esse produto é omitido dos resultados.

A nossa consulta ficará assim:

1.SELECT `produtos`.* FROM `produtos` INNER JOIN `categorias` ON `produtos`.`categoria_id` = `categorias`.`id` ORDER BY `produtos`.`nome` ASC

Sua tradução seria: SELECIONAR todas as colunas [da tabela produtos] da TABELA `produtos` UNINDO A TABELA `categorias` ONDE a coluna `categoria_id` [da tabela produtos] É IGUAL a coluna `id` [da tabela categorias] ORDENADO PELO `nome` [da tabela produtos] ASCENDETEMENTE

 

A nossa “regra de relação” acontece ali entre o ON e o ORDER BY, dizemos que a relação entre as tabelas usará como referencia a coluna “categoria_id” da tabela “produtos” sendo igual a coluna “id” da tabela “categorias”… Se você fosse usar algum WHERE ele entraria depois do ON e antes do ORDER BY.

Pra quem ainda não entendeu, o ON é como o WHERE de uma consulta normal… É a regra da relação.

Repare que agora precisamos usar um formato diferente para identificar as colunas usando: `tabela`.`coluna`… Isso é necessário pois agora estamos trabalhando com duas tabelas.

Da forma que a nossa consulta está ainda não estamos pegando o nome da categoria… fazemos isso adicionando mais um campo na parte do SELECT, assim:

1.SELECT `produtos`.*, `categorias`.`nome` FROM `produtos` INNER JOIN `categorias` ON `produtos`.`categoria_id` = `categorias`.`id` ORDER BY `produtos`.`nome` ASC

Agora estamos pegando também o valor da coluna “nome” do registro encontrado (pela relação) na tabela “categorias”.

 

Só que agora temos um novo problema… Nas duas tabelas existe uma coluna chamada “nome”, e quando estivermos lá no PHP, dentro do while, não teríamos como identificar de qual tabela pegamos as informações (veja a próxima imagem), pois as duas seriam $produto['nome']… Precisamos então renomear esse novo campo que adicionamos a busca, assim:

1.SELECT `produtos`.*, `categorias`.`nome` AS categoria FROM `produtos` INNER JOIN `categorias` ON `produtos`.`categoria_id` = `categorias`.`id` ORDER BY `produtos`.`nome` ASC

Agora o resultado de `categorias`.`nome` estará presente nos resultados como “categoria” e não “nome”… Sei que parece complicado de início mas vocês vão entender já já.

E por fim, faremos mais uma modificação, pra evitar ficar usando `tabela`.`coluna` também podemos renomear as tabelas, e com isso diminuir otamanho da consulta:

1.SELECT p.*, c.`nome` AS categoria FROM `produtos` AS p INNER JOIN `categorias` AS c ON p.`categoria_id` = c.`id` ORDER BY p.`nome` ASC

Nesse caso p representará a tabela “produtos” e c representará a “categorias”.

 

Sei que parece uma consulta maior e mais complicada… Mas você fará o MySQL trabalhar muito menos se fizer assim, com JOINS, do que fazer uma 2ª consulta dentro do while… Essa é a forma mais correta de fazer consultas quando precisamos de informações vindas de mais de uma tabela.

Agora vamos ao nosso novo script de PHP que, sem dúvidas, é bem mais prático e eficiente:

01.<?php
02.  
03.// Consulta que pega todos os produtos e o nome da categoria de cada um
04.$sql = "SELECT p.*, c.`nome` AS categoria FROM `produtos` AS p INNER JOIN `categorias` AS c ON p.`categoria_id` = c.`id` ORDER BY p.`nome` ASC";
05.$query = mysql_query($sql);
06.while ($produto = mysql_fetch_assoc($query)) {
07.    // Aqui temos o array $produto com todos os dados encontrados
08.    echo 'Titulo: ' . $produto['nome'] . '<br />';
09.    echo 'Preço: ' . $produto['preco'] . '<br />';
10.    echo 'Categoria: ' . $produto['categoria']. '<br />';
11.    echo '<hr />';
12.}
13.  
14.?>

Os outros tipos de JOINs

Existem também outros dois tipos de JOIN: o LEFT JOIN e o RIGHT JOIN:

Se usássemos o LEFT JOIN seriam retornados todos os produtos, independente se eles estão ligados a uma categoria (na tabela categorias) existente ou não.

Já o RIGHT JOIN seria exatamente o contrário: seriam retornados todos os produtos que pertencem categorias existentes e também o nome das outras categorias que não tem ligação com nenhum produto.

O uso desses outros tipos de JOIN é muito raro e acho que não vale a pena ficar filosofando sobre eles enquanto se aprende sobre relacionamentos.

E a relação com mais de duas tabelas?

Só pra exemplo, essa seria a consulta que pega os produtos, as categorias e o nome do usuário que cadastrou o produto e filtrando apenas pelos produtos ativos:

1.SELECT p.*, c.`nome` AS categoria, u.`nome` AS usuario FROM `produtos` AS p INNER JOIN `categorias` AS c ON p.`categoria_id` = c.`id` INNER JOIN `usuarios` AS u ON p.`usuario_id` = u.`id` WHERE (p.`ativo` = 1) ORDER BY p.`nome` ASC

 

Fonte:thiagobelem.net

NOTICIAS RELACIONADAS:

  1. Usar MySQL com PHP

4 Respostas para “Relacionamento de Tabelas no MySQL”

  1. Parabéns Thiago pelo exelente tutorial, realmente vai me ajudar muito.
    Um abraço.
  2. Bacana o tutorial… mas eu tenho uma dúvida!

    No meu caso estou usando relacionamento n:n, entre as tabelas noticias e categorias, (tem uma terceira que liga as duas com o id).

    Eu estou querendo listar as ultimas noticias que são cadastradas, porém ordenando pelas categorias… mas sem repetir essas categorias. Pro exemplo: numa lista de 15 noticias, seria cada noticia com uma categoria.

    Eu fiz assim:

    SELECT n.id AS nid, n.titulo AS nt, n.chamada AS nc, n.data_criacao AS nd, c.id AS cid, c.titulo AS ct
    FROM noticias AS n
    JOIN noticias_categorias AS nc ON (n.id = nc.noticia_id)
    JOIN categorianoticias AS c ON (c.id = nc.categorianoticia_id) GROUP BY cid ORDER BY nid DESC LIMIT 15

    Dessa forma ele lista as ultimas 15 noticias sem repetir as categorias, porém se eu adicionar uma outra noticia com uma categoria já existente, essa notícia não aparece na lista.

    Tu sabe como fazer isso?

  3. [...] detalhes deste assunto. Você pode ver claramente como utilizar o inner join no mysql, neste link: http://brasiltutoriais.com.br/relacionamento-de-tabelas-no-mysql/ – Eu também não sabia como utlizar. Vale muito a pena [...]
  4. Parabéns! Excelente post!

Deixe seu comentário, compartilhe sua opinião

Spam Protection by WP-SpamFree

Parceiros:|BRA Concursos |Mural da Vila |Noticias de Urucui |Anita Mulher