Publicidade (Anuncie Aqui)

Relacionamento de Tabelas no MySQL

Relacionamento de Tabelas no MySQL

Opa opa! 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”:

CREATE TABLE `categorias` (
	`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
	`nome` VARCHAR( 255 ) NOT NULL
) ENGINE = MYISAM;

CREATE TABLE `produtos` (
	`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
	`categoria_id` INT NOT NULL ,
	`nome` VARCHAR( 255 ) NOT NULL ,
	`preco` DECIMAL( 10,2 ) NOT NULL
) ENGINE = MYISAM;

E vamos inserir alguns dados para exemplo:

-- Extraindo dados da tabela `categorias`
INSERT INTO `categorias` VALUES(1, 'Camisetas');
INSERT INTO `categorias` VALUES(2, 'Canecas');

-- Extraindo dados da tabela `produtos`
INSERT INTO `produtos` VALUES(1, 1, 'Camiseta Social', 15.00);
INSERT INTO `produtos` VALUES(2, 1, 'Camiseta Regata', 11.99);
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:

<?php

// Consulta que pega todos os produtos
$sql = "SELECT * FROM `produtos` ORDER BY `nome` ASC";
$query = mysql_query($sql);
while ($produto = mysql_fetch_assoc($query)) {
	// Aqui temos o array $produto com todos os valores do produto

	// Consulta para pegar os dados da categoria:
	$sqlC = "SELECT * FROM `categorias` WHERE `id` = " . $produto['categoria_id'];
	$queryC = mysql_query($sqlC);
	$categoria = mysql_fetch_assoc($queryC);

	echo 'Titulo: ' . $produto['nome'] . '<br />';
	echo 'Preço: ' . $produto['preco'] . '<br />';
	echo 'Categoria: ' . $categoria['nome']. '<br />';
	echo '<hr />';
}

?>

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:

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:

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:

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:

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:

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:

<?php

// Consulta que pega todos os produtos e o nome da categoria de cada um
$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";
$query = mysql_query($sql);
while ($produto = mysql_fetch_assoc($query)) {
	// Aqui temos o array $produto com todos os dados encontrados
	echo 'Titulo: ' . $produto['nome'] . '<br />';
	echo 'Preço: ' . $produto['preco'] . '<br />';
	echo 'Categoria: ' . $produto['categoria']. '<br />';
	echo '<hr />';
}

?>

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:

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

Sim.. eu adoro consultas gigantescas. :D

Você também vai gostar de ler:

Postado em Artigos, MySQL, PHP, Tutoriais

Com as tags , , , , , , , , , , ,

Escrito por Thiago Belem

Gostou desse artigo?

Não se esqueça de assinar o RSS e divulgue-o para o mundo:

View Comments

  1. Alex disse:

    parabens tiagão…ótimo tutorial…brother, e para inserir ?? ex: tenho tabela estado, cidade e bairro e quero cadastrar um novo bairro…mas q qdo eu informar o bairro e der um cadastrar, ele vai de acordo com o estado e cidade que eu escolhi…manda essa pra gente !!!

  2. Gilmar Angelo disse:

    Muito bom cara!
    Muito bom mesmo…
    ja adcionei nos favoritos.

    :D

  3. Olá Thiago, parabéns pelo artigo, está super explicado! Só tenho uma dúvida ou curiosidade que gostaria de tirar.

    Normalmente esses relacionamentos mais simples como esse, eu fazia usando apenas o WHERE e sempre funcionou blz, isso muda alguma coisa com relação ao desempenho das consultas do mysql?

  4. S-Bay disse:

    Hello tiago , parabens :) muito bom
    Estou aqui com uma duvida.
    Como é que eu poderia ver por exemplo só todas as camisetas?

  5. Edvaldo da Rosa disse:

    Parabéns, seu blog é muito bom!

    Sempre procurei algo desse tipo e sempre achei coisas complexas e estranhas. Você explicou tudo de forma bem clara.

  6. Jardim disse:

    Muito bom mesmo Thiago !
    Estou aprendendo ainda mas foi possível entender sim !
    Já corri muito a net mas foi esse teu post que me fez entender !
    E com relação ao uso de FK com com banco do tipo InnoDB, vc tem algum post que trata disso de forma tão clara quanto este aqui postado ?

    Valeu !

  7. Cida disse:

    Parabéns Thiago! Pela sua forma clara e pelo seu desprendimento em compartilhar o seu conhecimento.

    Acredito nas pessoas inteligentes e seguras como você, sem medo de ser feliz… :)

    Sucesso cada vez mais!

  8. Rogerio disse:

    muito grato, ja uso mysql a algum tempo, mas de forma bem rustica, sempre que precisei usar selects maiores sempre faço dentro do while e while… mas essa sua dica foi muito boa. agradeço!

  9. Seyfertt disse:

    Thiago, tudo bom? Tenho uma dúvida…

    Estou fazendo relacionamento 1:1, só que tenho um pequeno problema… É impossível deixar 2 colunas em “auto increment”, sempre que adiciono um novo usuário no banco de dados o ID de usuário fica certo, porem o Config_ID, fica sempre em 0. Tem alguma idéia de como posso arrumar isso?

  10. williams disse:

    Parabéns e continue assim /

  11. Thiago Silva disse:

    Irmão, parabéns! vc tem uma metodologia de ensino muito boa. Relmente vc sabe ensinar de uma forma prática e objetiva. Tô entrando nessa área de desenvolvimento de websites e gosto muito dessa área. Já faz algum tempo q eu tava querendo aprender como acontece o relacionamente entre tabelas no mysql e o seu artigo tirou muitas dúvidas minhas. Sou técnico em informática na área de programação e não tive muitas aulas de programação web, mas tô aprendendo estudando sozinho… Ano q vem vou começar o curso superior de programação web. Parabéns pelos artigos. São excelentes. Um abraço.

  12. Seyfertt disse:

    Ótimo artigo Thiago, agora sim entendo como fazer uma relação em tabelas.

    Grande abraço

  13. Robson Ramos Barreto disse:

    Thiago Parabéns, Agradeço pelos vários ótimos Post !!

    Abraço

  14. Thiago Machado disse:

    Parabéns pelo artigo muito bem explicado, sempre que possivel visitarei o blog.

  15. Tulio disse:

    Parabéns pelo blog, todos os artigos são muito interessantes. Somente uma pergunta: Não seria interessante utilizar nos casos com integridade referencial Engine = InnoDB e definir uma chave estrangeira?

    • Thiago Belem disse:

      @Tulio
      Realmente existe essa parte de integridade referencial, mas eu acho que pra quem tá começando isso pode parecer muito complexo… Essas relações acabam obrigando o usuário a seguir uma ordem de raciocinio e ações caso eles queiram fazer alguma alteração direto no BD. Mas assim que possível falarei sobre. :]

  16. Fillipe Moraes disse:

    Eu sempre usei (agora vejo que erroneamente) um while dentro de outro.. mas vejo que sempre cometi esse erro por não ter alguma boa explicação como essa.. entendi absolutamente tudo e achei mto fácil!

    Agradeço e estou visitando seu blog pelo menos 3x por semana!
    Valeu!

  17. Renato Reis disse:

    Muito bem explicado mesmo, gosto mto do JOIN, realmente torna as paradas mais legais, e a forma que você explica, não só mostrando o código, mas sim mostrando a gente como tudo funcionando (“ensinando a pescar e não dando o peixe”) acho muito interessante. Vou tentar executar mais esse português literal pra programação que ajuda bastante na hora de pensar.
    Parabéns!

  18. Evandro disse:

    se vc tivesse postado isso no seu blog enqt eu tava tentando aprender o funcionamento de JOIN, com toda certeza não teria demorado 2h pra entender hehehehehehehe, parabéns pela iniciativa… excelente blog

  19. Evandro disse:

    Thiago, um artigo melhor do que o outro.
    PARABÉNS! TRABALHO FANTÁSTICO ;)

  20. Muito bem explicado.
    Não tem quem não vá entender.

    Valeu.

blog comments powered by Disqus