Design do banco de dados: normalizando um relacionamento “(muitos para muitos) e muitos”


13

Versão curta

Eu tenho que adicionar um número fixo de propriedades adicionais a cada par em uma associação muitos-para-muitos existente. Passando para os diagramas abaixo, qual das Opções 1-4 é a melhor maneira, em termos de vantagens e desvantagens, de conseguir isso estendendo o Caso Base? Ou existe uma alternativa melhor que eu não considerei aqui?

Versão mais longa

Atualmente, tenho duas tabelas em um relacionamento muitos para muitos, por meio de uma tabela de junção intermediária. Agora preciso adicionar links adicionais às propriedades que pertencem ao par de objetos existentes. Eu tenho um número fixo dessas propriedades para cada par, embora uma entrada na tabela de propriedades possa ser aplicada a vários pares (ou mesmo ser usada várias vezes para um par). Estou tentando determinar a melhor maneira de fazer isso e estou tendo problemas para resolver como pensar na situação. Semanticamente, parece que posso descrevê-lo como um dos seguintes igualmente bem:

  1. Um par vinculado a um conjunto de um número fixo de propriedades adicionais
  2. Um par vinculado a muitas propriedades adicionais
  3. Muitos (dois) objetos vinculados a um conjunto de propriedades
  4. Muitos objetos vinculados a muitas propriedades

Exemplo

Eu tenho dois tipos de objetos, X e Y, cada um com IDs exclusivos, e uma tabela de vínculo objx_objycom colunas x_ide y_id, que juntos formam a chave primária do link. Cada X pode estar relacionado a muitos Ys e vice-versa. Essa é a configuração do meu relacionamento muitos-para-muitos.

Caso base

Caso base

Agora, adicionalmente, tenho um conjunto de propriedades definidas em outra tabela e um conjunto de condições sob as quais um determinado par (X, Y) deve ter a propriedade P. O número de condições é fixo e o mesmo para todos os pares. Eles basicamente dizem "Na situação C1, o par (X1, Y1) possui a propriedade P1", "Na situação C2, o par (X1, Y1) possui a propriedade P2" e assim por diante, para três situações / condições para cada par na junção mesa.

Opção 1

Na minha situação atual há exatamente três tais condições, e eu não tenho nenhuma razão para esperar que a aumentar, então uma possibilidade é adicionar colunas c1_p_id, c2_p_ide c3_p_idpara featx_featy, especificando para um determinado x_ide y_id, que a propriedade p_idpara uso em cada um dos três casos .

Opção 1

Isso não parece uma ótima idéia para mim, porque complica o SQL para selecionar todas as propriedades aplicadas a um recurso e não é facilmente escalável para mais condições. No entanto, ele impõe a exigência de um certo número de condições por par (X, Y). De fato, é a única opção aqui que faz isso.

opção 2

Crie uma tabela de condições conde adicione o ID da condição à chave primária da tabela de junção.

opção 2

Uma desvantagem disso é que ele não especifica o número de condições para cada par. Outra é que, quando estou apenas considerando o relacionamento inicial, com algo como

SELECT objx.*, objy.* FROM objx
  INNER JOIN objx_objy ON objx_objy.x_id = objx.id
  INNER JOIN objy ON objy.id = objx_objy.y_id

Em seguida, tenho que adicionar uma DISTINCTcláusula para evitar entradas duplicadas. Isso parece ter perdido o fato de que cada par deveria existir apenas uma vez.

Opção 3

Crie um novo 'ID do par' na tabela de junção e, em seguida, tenha uma segunda tabela de links entre a primeira e as propriedades e condições.

Opção 3

Isso parece ter o menor número de desvantagens, exceto a falta de impor um número fixo de condições para cada par. No entanto, faz sentido criar um novo ID que não identifique nada além dos existentes?

Opção 4 (3b)

Basicamente, o mesmo que a Opção 3, mas sem a criação do campo ID adicional. Isso é feito colocando os dois IDs originais na nova tabela de junção, para que ele contenha x_ide y_idcampos, em vez de xy_id.

Opção 4

Uma vantagem adicional desse formulário é que ele não altera as tabelas existentes (embora elas ainda não estejam em produção). No entanto, basicamente duplica uma tabela inteira várias vezes (ou parece que é assim mesmo) e também não parece ideal.

Sumário

Meu sentimento é que as opções 3 e 4 são semelhantes o suficiente para que eu pudesse ir com qualquer uma delas. Eu provavelmente teria agora, se não fosse a exigência de um número pequeno e fixo de links para propriedades, o que faz a Opção 1 parecer mais razoável do que seria. Com base em alguns testes muito limitados, a adição de uma DISTINCTcláusula às minhas consultas não parece afetar o desempenho nessa situação, mas não tenho certeza de que a Opção 2 represente a situação e as demais, devido à duplicação inerente causada pela colocação os mesmos pares (X, Y) em várias linhas da tabela de links.

É uma dessas opções o meu melhor caminho a seguir ou há outra estrutura que devo considerar?


No geral, 1 e 4 parecem as melhores opções, eu concordo. Não seria fácil impor o número fixo (3) de propriedades com a opção 4, mas acho que é viável.
precisa saber é o seguinte

Para a DISTINCTcláusula, eu estava pensando em uma consulta como a que está no final do nº 2, que vincula xe yatravés, xycmas não se refere a c... Então, se eu tiver (x_id, y_id, c_id)restrições UNIQUEcom linhas (1,1,1)e (1,1,2), então SELECT x.id, y.id FROM x JOIN xyc JOIN y, retornarei duas idênticas linhas (1,1), e (1,1).
Michael Underwood

1
Ah ok. Eu descartaria a opção 2 de qualquer maneira. Eu iria com 1 ou 4.
ypercubeᵀᴹ

Quanto mais penso nisso, mais sinto que restringir o número de propriedades a exatamente três é o menos importante dos meus requisitos. Portanto, exceto nos comentários construtivos adicionais nos próximos instantes, provavelmente irei com a quarta posição neste momento. Obrigado pela sua contribuição, @ypercube!
Michael Underwood

Respostas:


7
  • Opção 1

    * Isso não me parece uma ótima idéia, porque complica o SQL selecionar todas as propriedades aplicadas a um recurso…

    Isso não complica necessariamente a consulta SQL (veja a conclusão abaixo).

    ... e não é facilmente escalável para mais condições ...

    É facilmente escalável para mais condições, desde que ainda haja um número fixo de condições e não haja dezenas ou centenas.

    No entanto, ele impõe a exigência de um certo número de condições por par (X, Y). De fato, é a única opção aqui que faz isso. *

    É verdade, e embora você diga em um comentário que isso é "o menos importante dos meus requisitos", você não disse que isso não importa.

  • opção 2

    Uma desvantagem disso é que ele não especifica o número de condições para cada par. Outra é que, quando estou apenas considerando o relacionamento inicial ... preciso adicionar uma cláusula DISTINCT para evitar entradas duplicadas ...

    Eu acho que você pode descartar essa opção por causa das complicações mencionadas. A objx_objymesa é provável que seja a tabela de condução para algumas das suas consultas (por exemplo, "selecionar todas as propriedades aplicadas a um recurso", que estou tomando para dizer todas as propriedades aplicadas a uma objxou objy). Você pode usar uma visualização para pré-aplicar a DISTINCTconsulta, para que não seja uma questão de complicar as consultas, mas isso será muito ruim em termos de desempenho para obter muito pouco ganho.

  • Opção 3

    No entanto, faz sentido criar um novo ID que não identifique nada além dos existentes?

    Não, não - a opção 4 é melhor em todos os aspectos.

  • Opção 4

    … Basicamente duplica uma tabela inteira várias vezes (ou parece que é assim mesmo) e também não parece ideal.

    Essa opção é ótima - é a maneira óbvia de configurar as relações se o número de propriedades for variável ou sujeito a alterações

Conclusão

Minha preferência seria a opção 1 se o número de propriedades por objx_objyprovavelmente for estável, e se você não puder imaginar adicionar mais do que um punhado extra. Também é a única opção que impõe a restrição 'número de propriedades = 3' - impor uma restrição semelhante na opção 4 provavelmente envolveria a adição de c1_p_id... colunas à tabela xy de qualquer maneira *.

Se você realmente não se importa muito com essa condição, e também tem motivos para duvidar que o número de condições da propriedade seja estável, escolha a opção 4.

Se você não tiver certeza de qual, escolha a opção 1 - é mais simples e definitivamente melhor se você tiver a opção, como outros já disseram. Se você deixar a opção 1 "... porque complicar o SQL para selecionar todas as propriedades aplicadas a um recurso ...", sugiro a criação de uma exibição para fornecer os mesmos dados que a tabela extra na opção 4:

tabelas da opção 1:

create table prop(id integer primary key);
create table objx(id integer primary key);
create table objy(id integer primary key);

create table objx_objy(
  x_id integer references objx
, y_id integer references objy
, c1_p_id integer not null references prop
, c2_p_id integer not null references prop
, c3_p_id integer not null references prop
, primary key (x_id, y_id)
);

insert into prop(id) select generate_series(90,99);
insert into objx(id) select generate_series(10,12);
insert into objy(id) select generate_series(20,22);

insert into objx_objy(x_id,y_id,c1_p_id,c2_p_id,c3_p_id)
select objx.id, objy.id, 90, 91, 90+floor(random()*10)
from objx cross join objy;

vista para 'emular' opção 4:

create view objx_objy_prop as
select x_id
     , y_id
     , unnest(array[1,2,3]) c_id
     , unnest(array[c1_p_id,c2_p_id,c3_p_id]) p_id
from objx_objy;

"selecione todas as propriedades aplicadas a um recurso":

select distinct p_id from objx_objy_prop where x_id=10 order by p_id;

/*
|p_id|
|---:|
|  90|
|  91|
|  97|
|  98|
*/

dbfiddle aqui


-3

Acredito que qualquer uma dessas opções poderia funcionar, mas eu iria com a opção 1 se o número de condições for realmente fixado em 3 e a opção 2, se não for. O aparelho de barbear da Occam também funciona para o design de banco de dados, todos os outros fatores sendo iguais ao design mais simples geralmente são os melhores.

Embora se você deseja seguir regras rígidas de normalização do banco de dados, acredito que você precisaria usar 2, independentemente de o número de condições ser fixo.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.