Estou trabalhando para atualizar o banco de dados de produtos do nosso site. Ele é construído no MySQL, mas essa é mais uma questão geral de padrão de design de banco de dados.
Estou pensando em mudar para um padrão Supertype / Subtype. Nosso banco de dados atual / anterior é principalmente uma tabela única que possui dados sobre um único tipo de produto. Estamos buscando expandir nossa oferta de produtos para incluir produtos diferentes.
Esse novo design de rascunho é assim:
Product product_[type] product_attribute_[name]
---------------- ---------------- ----------------------------
part_number (PK) part_number (FK) attributeId (PK)
UPC specific_attr1 (FK) attribute_name
price specific_attr2 (FK)
... ...
Eu tenho uma pergunta sobre as tabelas de atributos do produto. A idéia aqui é que um produto possa ter uma lista de atributos, como cor: vermelho, verde, azul ou material: plástico, madeira, cromo, alumínio, etc.
Essa lista seria armazenada em uma tabela e a chave primária (PK) para esse item de atributo será usada na tabela específica do produto como uma chave estrangeira (FK).
(O livro Patterns of Enterprise Application Architecture, de Martin Fowler, chama isso de " Mapeamento de Chave Estrangeira ")
Isso permite que uma interface de site puxe a lista de atributos para um determinado tipo de atributo e cuspa em um menu suspenso ou em outro elemento da interface do usuário. Essa lista pode ser considerada uma lista "autorizada" de valores de atributo.
O número de junções que acaba acontecendo ao puxar um produto específico parece excessivo para mim. Você deve associar todas as tabelas de atributos do produto ao produto para poder obter os campos desse atributo. Geralmente, esse campo pode ser apenas uma string (varchar) para seu nome.
Esse padrão de design acaba criando um grande número de tabelas, assim como você acaba com uma tabela para cada atributo. Uma idéia para neutralizar isso seria criar algo mais como uma tabela de “sacolas” para todos os atributos do produto. Algo assim:
product_attribute
----------------
attributeId (PK)
name
field_name
Dessa forma, sua tabela pode ficar assim:
1 red color
2 blue color
3 chrome material
4 plastic material
5 yellow color
6 x-large size
Isso pode ajudar a reduzir a fluência da tabela, mas não reduz o número de junções e parece um pouco errado combinar tantos tipos diferentes em uma única tabela. Mas você seria capaz de obter todos os atributos de "cores" disponíveis com bastante facilidade.
No entanto, pode haver um atributo que tenha mais campos do que apenas "nome", como o valor RGB de uma cor. Isso exigiria que esse atributo específico possuísse outra tabela ou um único campo para o nome: par de valores (que possui suas próprias desvantagens).
O último padrão de design em que posso pensar é armazenar o valor real do atributo na tabela específica do produto e não ter uma "tabela de atributos". Algo assim:
Product product_[type]
---------------- ----------------
part_number (PK) part_number (FK)
UPC specific_attr1
price specific_attr2
... ...
Em vez de uma chave estrangeira para outra tabela, ela conteria o valor real, como:
part_number color material
----------- ----- --------
1234 red plastic
Isso eliminaria as junções e impediria a fluência da tabela (talvez?). No entanto, isso evita ter uma "lista autorizada" de atributos. Você pode retornar todos os valores inseridos no momento para um determinado campo (por exemplo: cor), mas isso também elimina a idéia de ter uma "lista autorizada" de valores para um determinado atributo.
Para ter essa lista, você ainda precisa criar uma tabela de atributos "grab bag" ou ter várias tabelas (subida de tabela) para cada atributo.
Isso cria a maior desvantagem (e por que eu nunca usei essa abordagem) de agora ter o nome do produto em vários locais.
Se você tiver o valor de cor "vermelho" na "tabela de atributos principais" e também armazená-lo na tabela "produto_ [tipo]", uma atualização na tabela "principal" causará um possível problema de integridade de dados se o aplicativo não atualize todos os registros com o valor antigo na tabela "product_type" também.
Então, após minha longa explicação e análise sobre esse cenário, percebo que esse não pode ser um cenário incomum e pode haver até um nome para esse tipo de situação.
Existem soluções geralmente aceitas para esse desafio de design? O número potencialmente grande de junções é aceitável se as tabelas forem relativamente pequenas? O armazenamento do nome do atributo, em vez de um atributo PK, é aceitável em alguma situação? Existe outra solução em que não estou pensando?
Algumas notas sobre o banco de dados / aplicativo deste produto:
- Os produtos não são atualizados / adicionados / removidos com frequência
- Os atributos não são atualizados / adicionados / removidos com frequência
- A tabela é mais frequentemente consultada para ler / retornar informações
- O cache do servidor está ativado para armazenar em cache o resultado de uma determinada consulta / resultado
- Pretendo começar com apenas um tipo de produto e estender / adicionar outros ao longo do tempo e terei potencialmente mais de 10 tipos diferentes
fonte
Respostas:
Eu pessoalmente usaria um modelo semelhante ao seguinte:
A tabela de produtos seria bem básica, os principais detalhes do produto:
Segundo a tabela de atributos para armazenar cada um dos diferentes atributos.
Por fim, crie a tabela product_attribute como a tabela JOIN entre cada produto e seus atributos associados a ele.
Dependendo de como você deseja usar os dados, você está vendo duas junções:
Consulte SQL Fiddle com demonstração . Isso retorna dados no formato:
Mas se você quiser retornar os dados em um
PIVOT
formato em que tenha uma linha com todos os atributos como colunas, poderá usarCASE
instruções com um agregado:Consulte SQL Fiddle com demonstração . Os dados são retornados no formato:
Como você pode ver, os dados podem estar em um formato melhor para você, mas se você tiver um número desconhecido de atributos, ele se tornará facilmente insustentável devido a nomes de atributos codificados, então no MySQL você pode usar instruções preparadas para criar dinâmicas dinâmicas . Seu código seria o seguinte (consulte SQL Fiddle With Demo ):
Isso gera o mesmo resultado da segunda versão, sem a necessidade de codificar nada. Embora existam muitas maneiras de modelar isso, acho que esse design de banco de dados é o mais flexível.
fonte
Eu expandiria a resposta de Taryn e modificaria a tabela de atributos para ter a coluna fk_attribute_type_id que será em vez da coluna attribute_name e apontará para a nova tabela attribute_type.
Portanto, você estruturou os tipos de atributo em uma tabela e pode alterá-lo a qualquer momento em um só lugar.
Na minha opinião, é melhor trabalhar com o tipo de "discagem" (tabela com tipos possíveis) do que com o tipo enum (como na coluna attribute_name (e ainda por cima, na verdade, não é o nome, o tipo de atributo)).
fonte