Os relacionamentos um-para-muitos são um dos relacionamentos de banco de dados mais comuns. Se você quiser saber quando e como usar relacionamentos um-para-muitos, este artigo é um ótimo ponto de partida.
Você certamente usará relacionamentos um-para-muitos para armazenar informações em qualquer banco de dados relacional, seja projetando software de nível empresarial ou apenas criando um banco de dados simples para acompanhar a coleção de selos de seu tio.
Uma breve introdução ao modelo relacional
Os bancos de dados relacionais são um componente central de qualquer aplicativo transacional moderno. O modelo relacional é composto por tabelas (dados organizados em linhas e colunas) que possuem pelo menos uma chave única que identifica cada linha. Cada tabela representa uma entidade. Isso é mostrado no exemplo a seguir, uma versão muito simples de uma tabela que representa os pedidos dos clientes:
O diagrama acima, que criei online usando o Vertabelo, tem uma única tabela. Cada linha na tabela representa um pedido e cada coluna (também conhecida como atributo ) representa cada informação individual contida em um pedido.
Para quem ainda não conhece a ferramenta de design Vertabelo, o artigo Quais são os símbolos usados nos diagramas ER? explica os símbolos e convenções usados. Você também pode querer aprender mais sobre modelos relacionais e bancos de dados usando nosso curso de modelagem de banco de dados.
O que são relacionamentos e por que precisamos deles?
Se examinarmos mais profundamente a tabela usada no exemplo anterior, veremos que ela não representa realmente um pedido completo. Ele não tem todas as informações que você esperaria que ele tivesse. Você notará que não inclui nenhum dado relacionado ao cliente que fez o pedido, nem nada sobre os produtos ou serviços solicitados.
O que devemos fazer para concluir este design para armazenar os dados do pedido? Devemos adicionar informações do cliente e do produto ao Pedido tabela? Isso exigiria a adição de novas colunas (atributos) para nomes de clientes, identificadores fiscais, endereços etc., conforme mostrado abaixo:
"ID do pedido" | "Data do pedido" | "Valor do pedido" | Cliente | "Endereço do cliente" | "Telefone do Cliente" | "Identificador Fiscal" |
---|---|---|---|---|---|---|
1 | 23 de junho | US$ 10 248,15 | Serviços Internacionais Ltda | 1247 St River Blvd, Charlotte, Carolina do Norte | (555) 478-8741 | IS789456 |
2 | 27 de junho | US$ 14 785,45 | World Master Importing Inc. | 354 Mountain Hill Rd, Los Angeles, CA | (555) 774-8888 | WM321456 |
3 | jul-01 | US$ 7 975,00 | First State Provisioning Llc | 444 North Highway, Houston, TX | (555) 698-7411 | FS947561 |
4 | jul-03 | US$ 6 784,25 | Serviços Internacionais Ltda | 1247 St River Blvd, Charlotte, Carolina do Norte | (555) 478-8741 | IS789456 |
5 | jul-07 | US$ 21 476,10 | World Master Importing Inc. | 354 Mountain Hill Rd, Los Angeles, CA | (555) 774-8888 | WM321456 |
6 | jul-12 | US$ 9 734,00 | First State Provisioning Llc | 444 North Highway, Houston, TX | (555) 698-7411 | FS947561 |
7 | julho-17 | US$ 14 747,45 | World Master Importing Inc. | 354 Mountain Hill Rd, Los Angeles, CA | (555) 774-8888 | WM321456 |
8 | 21 de julho | US$ 19 674,85 | Serviços Internacionais Ltda | 1247 St River Blvd, Charlotte, Carolina do Norte | (555) 478-8741 | IS789456 |
Se fizermos isso, logo teremos problemas. A maioria dos clientes faz mais de um pedido, portanto, esse sistema armazenará as informações do cliente muitas vezes, uma vez para cada pedido de cada cliente. Isso não parece uma jogada inteligente.
Além disso, o que acontece quando um cliente altera seu número de telefone? Se alguém precisar ligar para o cliente, poderá encontrar o número antigo em pedidos anteriores – a menos que alguém atualize centenas (ou até milhares) de pedidos existentes com as novas informações. E o mesmo valeria para qualquer outra mudança.
Um modelo relacional exige que definamos cada entidade como uma tabela separada e estabeleça relacionamentos entre elas. Armazenar todas as informações em uma única tabela simplesmente não funciona.
Existem vários tipos de relacionamentos entre tabelas, mas provavelmente o mais comum é o relacionamento um-para-muitos, que geralmente é escrito como 1:N. Esse tipo de relacionamento significa que uma linha em uma tabela (geralmente chamada de tabela pai) pode ter um relacionamento com muitas linhas em outra tabela (geralmente chamada de tabela filha). Alguns exemplos comuns de relacionamentos um-para-muitos são:
- Um fabricante de automóveis fabrica muitos modelos diferentes, mas um modelo de carro específico é fabricado apenas por um único fabricante de automóveis.
- Um cliente pode fazer várias compras, mas cada compra é feita por um único cliente.
- Uma empresa pode ter vários números de telefone, mas um número de telefone pertence a uma empresa.
Existem também outros tipos de relacionamentos entre tabelas; se você quiser saber mais sobre eles, veja este artigo sobre relacionamentos muitos-para-muitos.
Voltando ao nosso exemplo de pedido inicial, o
Customer
table seria a tabela pai e o Order
mesa a criança; um cliente pode ter muitos pedidos, enquanto um pedido pode pertencer a um único cliente. Observe que a definição de um para muitos permite que uma linha na tabela pai seja associada a muitas linhas em cada tabela filha, mas não exige isso. Na verdade, o design permite que um cliente tenha zero pedidos (ou seja, um novo cliente que ainda não fez sua primeira compra), um pedido (um cliente relativamente novo que fez uma única compra) ou muitos pedidos (um cliente frequente).
Mostrando relacionamentos um-para-muitos em um diagrama ER
Vamos dar uma olhada em um exemplo mais completo de um sistema simples de pedidos de clientes usando um diagrama ER (ou relacionamento de entidade). (Se você quiser saber mais sobre esses diagramas, Vertabelo Features:Logical Diagrams é um ótimo ponto de partida.) Aqui está o modelo:
Este é um design mais realista. Você notará que há novas entidades (tabelas) no diagrama, que agora contém as tabelas
Customer
, Order
, Order Detail
e Product
. No entanto, a coisa mais importante que você percebe é que agora existem relacionamentos entre as tabelas . Em um modelo de banco de dados, os relacionamentos são representados por linhas conectando duas entidades. As características desses relacionamentos são representadas por diferentes conectores:
- Quando há uma única linha vertical, a entidade mais próxima desse conector tem apenas uma linha afetada pelo relacionamento. É o "um" em um para muitos.
- Quando há um conector de várias linhas que se parece com um pé de galinha, a entidade mais próxima desse conector tem várias linhas afetadas pelo relacionamento; são os 'muitos'.
Observando a imagem e conhecendo a notação, é simples entender que o diagrama define que cada
Order
pode ter muitos Order Detail
e que cada Order Detail
pertence a um único Order
. Implementando um relacionamento um-para-muitos entre tabelas
Para definir um relacionamento um-para-muitos entre duas tabelas, a tabela filha deve fazer referência a uma linha na tabela pai. Os passos necessários para defini-lo são:
- Adicione uma coluna à tabela filha que armazenará o valor do identificador primário. (Na verdade, a maioria dos mecanismos de banco de dados permite que seja qualquer chave exclusiva da tabela pai, não apenas a chave primária.) A coluna pode ser definida como obrigatória dependendo de suas necessidades de negócios; mesmo assim, as colunas de chave estrangeira geralmente são feitas
Observação: É uma boa prática manter o nome das colunas de referência igual ao da tabela (pai) referenciada. Isso torna ainda mais simples entender a relação.
- Adicione uma chave estrangeira restrição na tabela filho. Isso indica que cada valor armazenado nesta nova coluna faz referência a uma linha na tabela pai.
As restrições de chave estrangeira são um recurso disponível no banco de dados relacional que impõe que:
- Quando você adiciona uma linha à tabela filha, o valor da coluna de referência deve corresponder a um (e apenas um) valor na tabela pai. (É por isso que uma coluna ou conjunto de colunas que compõem uma chave primária ou chave única devem ser referenciados).
- Se alguém tentar excluir uma linha da tabela pai ou tentar modificar os valores da chave única/primária usada como referência e houver uma tabela filha que faça referência a essa linha, a operação falhará.
Esses dois recursos garantem que o banco de dados mantenha sua integridade. Não há possibilidade de criar pedidos referenciando um cliente inexistente, nem de deletar um cliente que já tenha pedidos.
Criando uma chave estrangeira
A sintaxe de chave estrangeira geralmente depende do mecanismo de banco de dados de destino. Depois de definir seu modelo lógico, você pode usar o recurso “Gerar modelo físico…” nos diagramas lógicos Vertabelo para transformar seu modelo (independente de banco de dados) em um modelo físico que corresponda ao seu provedor de banco de dados. O Vertabelo também gerará o script SQL necessário que permitirá criar as tabelas e relacionamentos em seu banco de dados de destino.
Alguns exemplos práticos de relacionamentos 1:N
Agora vamos rever alguns exemplos de relacionamentos um-para-muitos do mundo real.
Relação um-para-muitos usando chaves primárias
Este é provavelmente o cenário mais comum ao definir um relacionamento um-para-muitos. A tabela filho usa o valor da chave primária da tabela pai para estabelecer o relacionamento.
Este exemplo descreve um serviço de streaming online básico. Vamos revisar o que está armazenado em cada uma das tabelas e como elas se relacionam com as outras tabelas em nosso modelo:
- Cada
ServiceType
define como uma conta "se comporta" (por exemplo, quantos usuários podem se conectar ao sistema ao mesmo tempo, se a conta tiver Full HD ativado, etc.). Tem uma relação com outras entidades:- Um relacionamento de um para muitos com a
Account
, o que significa que cada tipo de serviço pode ter muitas contas desse tipo.
- Um relacionamento de um para muitos com a
- Cada
Account
armazena informações sobre um cliente. Possui dois relacionamentos diretos com outras entidades:- Cada conta pertence a um único
ServiceType
, conforme explicado acima. - Esta tabela tem uma relação um-para-muitos com o
Profile
table, o que significa que mais de um usuário pode se conectar ao nosso sistema usando a mesma conta.
- Cada conta pertence a um único
- Cada
Profile
representa um usuário em nosso sistema. Possui dois relacionamentos com outras entidades:- Cada perfil pertence a uma única
Account
. Isso permite que todos os membros da família (ou talvez um grupo de amigos) compartilhem a mesma conta, enquanto cada um tem seus próprios atributos pessoais (por exemplo, um nome de perfil). - Cada perfil tem um
Avatar
exclusivo .
- Cada perfil pertence a uma única
- Cada
Avatar
é uma imagem que nos permite identificar rapidamente cada usuário da conta. Tem um relacionamento com outra entidade:- Um relacionamento um-para-muitos com
Profile
, o que significa que um único avatar pode ser atribuído a perfis em contas diferentes.
- Um relacionamento um-para-muitos com
Relacionamentos um-para-muitos com chaves únicas naturais ou substitutas
O uso de chaves primárias substitutas é uma maneira amplamente aceita de modelagem de tabelas. (As chaves primárias substitutas são geradas pelo banco de dados e não têm valor comercial real.) Esse método produz chaves mais simples de usar e adiciona alguma flexibilidade para quando as alterações são necessárias.
No entanto, existem situações - por exemplo. quando precisamos interagir com sistemas externos – onde usar uma chave gerada em nosso banco de dados é uma abordagem ruim. Para esses cenários, geralmente é melhor usar chaves naturais, que são valores únicos que fazem parte da entidade que está sendo armazenada e não são gerados automaticamente pelo nosso banco de dados.
O exemplo a seguir representa um modelo de dados básico de uma organização que acompanha os veículos (ou seja, marca, modelo, cor e ano do carro), seus proprietários e quaisquer infrações de trânsito associadas. Quando a definimos, usamos chaves primárias substitutas para estabelecer as relações entre os veículos e marcas, modelos e proprietários, pois todas essas informações são tratadas internamente pelo nosso sistema.
Neste sistema, como um policial de outra cidade pode denunciar um carro estacionado ilegalmente usando nossa chave primária de veículo (
VehicleID
)? Tal informação não está naturalmente disponível no veículo estacionado, mas a placa está lá. Isso significa que a maneira mais simples de receber e associar informações de uma fonte externa (neste exemplo, qualquer departamento de polícia do país) é usar uma chave única natural em vez de uma chave primária substituta. A implementação física deste diagrama lógico para SQL Server está disponível aqui:
Relações de um para muitos na mesma mesa
Os exemplos anteriores focaram em relacionamentos entre duas ou mais tabelas, mas também existem cenários em que o relacionamento ocorre entre linhas da mesma tabela. Esse tipo de relacionamento um-para-muitos também é chamado de relacionamento hierárquico; ele é usado em muitos sistemas para representar estruturas semelhantes a árvores, ou seja, um organograma, uma conta do razão geral ou um produto e seus componentes.
A primeira vez que você precisar criar esse tipo de estrutura, ficará tentado a definir uma tabela para cada um dos níveis de sua hierarquia, conforme mostrado no diagrama a seguir:
Existem muitos problemas nessa abordagem:
- Todas as tabelas são quase idênticas e armazenam informações idênticas.
- Se sua organização adicionar um novo nível, você terá que modificar o modelo de dados e adicionar uma nova tabela, novas chaves estrangeiras etc.
- Se um funcionário receber uma promoção, você precisará excluí-lo de uma tabela e inseri-lo em outra.
Portanto, a melhor maneira de modelar esse tipo de estrutura é usar uma única tabela que faça referência a si mesma, conforme mostrado neste diagrama:
Aqui vemos um único
Employee
tabela e uma coluna chamada EmployeeID_Manager
. Essa coluna faz referência a outro funcionário da mesma organização que é o supervisor/gerente do funcionário atual. Eu adicionei o
_Manager
sufixo para distinguir entre o ID da linha atual e o ID do gerenciador. (Poderíamos usar ManagerID
em vez disso, mas prefiro manter o nome original da coluna referenciada e, nos casos em que ambas estão na mesma tabela, adicionar um sufixo que explique a função que ela realmente possui). Compreender os relacionamentos hierárquicos é mais complexo do que outros relacionamentos um-para-muitos. Mas se você esquecer a tabela onde todas as informações estão armazenadas e imaginar que na verdade são tabelas diferentes, cada uma delas representando um nível na hierarquia, fica um pouco mais fácil de visualizar. Imagine que você faça o relacionamento entre duas entidades e depois as combine em uma entidade.
O que vem a seguir?
Os exemplos fornecidos ajudarão você a identificar diferentes cenários que exigem um relacionamento um-para-muitos. Você pode começar a projetar sua própria estrutura de banco de dados usando o Vertabelo Database Modeler, uma ferramenta baseada na web que permite não apenas gerar um modelo lógico, mas também criar uma versão física dele para o provedor de banco de dados que você precisa.
Agora é a sua vez – use a seção de comentários para nos contar sobre seus pensamentos sobre este artigo, fazer perguntas adicionais ou compartilhar suas experiências de modelagem de banco de dados.