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()! ![]()
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. ![]()
Você também vai gostar de ler:
- PHP e MySQL para iniciantes – Consulta Avançada
- Entendendo a paginação de registros no MySQL
- Cadastrando múltiplos registros no MySQL ao mesmo tempo
- Como criar um Sistema de Login com Níveis de Permissão
- PHP e MySQL para iniciantes – Consulta Simples
Postado em Artigos, MySQL, PHP, Tutoriais
Com as tags Artigo, Busca, Consultas, INSERT, MySQL, PHP, Relacionamento, Relationship, Resultados, Scripts, SQL, Tutorial
Escrito por Thiago Belem
Gostou desse artigo?
Não se esqueça de assinar o RSS e divulgue-o para o mundo:

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 !!!
Muito bom cara!
Muito bom mesmo…
ja adcionei nos favoritos.
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?
@Jackson
Acredito que fazer com o JOIN seja mais correto já que o WHERE é pra filtrar os resultados.
Hello tiago , parabens
muito bom
Estou aqui com uma duvida.
Como é que eu poderia ver por exemplo só todas as camisetas?
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.
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 !
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!
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!
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?
Parabéns e continue assim /
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.
Ótimo artigo Thiago, agora sim entendo como fazer uma relação em tabelas.
Grande abraço
Thiago Parabéns, Agradeço pelos vários ótimos Post !!
Abraço
Parabéns pelo artigo muito bem explicado, sempre que possivel visitarei o blog.
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?
@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. :]
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!
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!
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
Thiago, um artigo melhor do que o outro.
PARABÉNS! TRABALHO FANTÁSTICO
Muito bem explicado.
Não tem quem não vá entender.
Valeu.