Como projetar uma tabela de produtos para vários tipos de produtos, onde cada produto possui muitos parâmetros

Eu não tenho muita experiência em design de tabelas. Meu objective é criar uma ou mais tabelas de produtos que atendam aos requisitos abaixo:

  • Suporte muitos tipos de produtos (TV, telefone, PC, …). Cada tipo de produto tem um conjunto diferente de parâmetros, como:

    • Telefone terá cor, tamanho, peso, OS …

    • PC terá CPU, HDD, RAM …

  • O conjunto de parâmetros deve ser dynamic. Você pode adicionar ou editar qualquer parâmetro que desejar.

Como posso atender a esses requisitos sem uma tabela separada para cada tipo de produto?

Você tem pelo menos essas cinco opções para modelar a hierarquia de tipos que descreve:

  • Herança de tabela única : uma tabela para todos os tipos de produto, com colunas suficientes para armazenar todos os atributos de todos os tipos. Isso significa muitas colunas, a maioria das quais são NULL em qualquer linha.

  • Herança de tabela de classs : uma tabela para Produtos, armazenando atributos comuns a todos os tipos de produtos. Em seguida, uma tabela por tipo de produto, armazenando atributos específicos para esse tipo de produto.

  • Herança de tabela concreta : nenhuma tabela para atributos comuns de produtos. Em vez disso, uma tabela por tipo de produto, armazenando atributos comuns do produto e atributos específicos do produto.

  • LOB Serializado : Uma tabela para Produtos, armazenando atributos comuns a todos os tipos de produtos. Uma coluna extra armazena um BLOB de dados semiestruturados, em XML, YAML, JSON ou algum outro formato. Este BLOB permite que você armazene os atributos específicos de cada tipo de produto. Você pode usar padrões de design sofisticados para descrever isso, como Fachada e Memento. Mas independentemente disso, você tem um blob de atributos que não podem ser consultados facilmente no SQL; você tem que buscar todo o blob de volta para o aplicativo e resolvê-lo lá.

  • Entidade-Atributo-Valor : Uma tabela para Produtos e uma tabela que giram atributos para linhas, em vez de colunas. EAV não é um projeto válido em relação ao paradigma relacional, mas muitas pessoas o usam de qualquer maneira. Este é o “Padrão de Propriedades” mencionado por outra resposta. Veja outras questões com a tag eav no StackOverflow para algumas das armadilhas.

Eu escrevi mais sobre isso em uma apresentação, Extensible Data Modeling .


Pensamentos adicionais sobre o EAV: Embora muitas pessoas pareçam favorecer o EAV, eu não o faço. Parece a solução mais flexível e, portanto, a melhor. No entanto, tenha em mente o adágio TANSTAAFL . Aqui estão algumas das desvantagens do EAV:

  • Nenhuma maneira de tornar uma coluna obrigatória (equivalente a NOT NULL ).
  • Nenhuma maneira de usar tipos de dados SQL para validar inputs.
  • Nenhuma maneira de garantir que os nomes de atributos sejam escritos consistentemente.
  • Nenhuma maneira de colocar uma chave estrangeira nos valores de um determinado atributo, por exemplo, para uma tabela de pesquisa.
  • Buscar resultados em um layout tabular convencional é complexo e caro, porque para obter atributos de várias linhas, você precisa fazer JOIN para cada atributo.

O grau de flexibilidade que o EAV oferece requer sacrifícios em outras áreas, provavelmente tornando seu código tão complexo (ou pior) do que teria sido para resolver o problema original de uma maneira mais convencional.

E na maioria dos casos, é desnecessário ter esse grau de flexibilidade. Na pergunta do OP sobre os tipos de produto, é muito mais simples criar uma tabela por tipo de produto para atributos específicos do produto, de modo que você tenha alguma estrutura consistente imposta pelo menos para inputs do mesmo tipo de produto.

Eu usaria o EAV somente se cada linha tivesse permissão para potencialmente ter um conjunto distinto de atributos. Quando você tem um conjunto finito de tipos de produtos, o EAV é um exagero. Herança de tabela de class seria minha primeira escolha.

@Coração de pedra

Eu iria aqui com EAV e MVC todo o caminho.

@Bill Karvin

Aqui estão algumas das desvantagens do EAV:

 No way to make a column mandatory (equivalent of NOT NULL). No way to use SQL data types to validate entries. No way to ensure that attribute names are spelled consistently. No way to put a foreign key on the values of any given attribute, eg 

para uma tabela de pesquisa.

Todas essas coisas que você mencionou aqui:

  • data de validade
  • validação de ortografia de nomes de atributos
  • colunas obrigatórias / campos
  • lidar com a destruição de atributos dependentes

na minha opinião, não pertenço a nenhum database porque nenhum database é capaz de lidar com essas interações e requisitos em um nível adequado, como faz uma linguagem de programação de um aplicativo.

Na minha opinião, usar um database dessa maneira é como usar uma pedra para marcanvasr um prego. Você pode fazer isso com uma pedra, mas você não deve usar um martelo mais preciso e especificamente projetado para esse tipo de atividade?

Buscar resultados em um layout tabular convencional é complexo e caro, porque para obter atributos de várias linhas, você precisa fazer JOIN para cada atributo.

Esse problema pode ser resolvido fazendo poucas consultas em dados parciais e processando-os em um layout tabular com seu aplicativo. Mesmo se você tiver 600 GB de dados do produto, poderá processá-los em lotes se precisar de dados de todas as linhas dessa tabela.

Indo mais longe Se você deseja melhorar o desempenho das consultas, pode selecionar determinadas operações como, por exemplo, relatórios ou pesquisa de texto global e preparar tabelas de índices que armazenariam os dados necessários e seriam regenerados periodicamente, digamos a cada 30 minutos.

Você nem precisa se preocupar com o custo de armazenamento de dados extra, porque fica mais barato e mais barato a cada dia.

Se você ainda estiver preocupado com o desempenho das operações feitas pelo aplicativo, você sempre poderá usar Erlang, C ++, Go Language para pré-processar os dados e, posteriormente, apenas processar os dados otimizados ainda mais em seu aplicativo principal.

Se eu usar o significado da Class Table Inheritance :

uma tabela para Produtos, armazenando atributos comuns a todos os tipos de produtos. Em seguida, uma tabela por tipo de produto, armazenando atributos específicos para esse tipo de produto. -Bill Karwin

Eu gosto do melhor das Sugestões de Bill Karwin. Eu posso antecipar uma desvantagem, que tentarei explicar como evitar que se torne um problema.

Qual plano de contingência devo ter no lugar quando um atributo que é comum apenas para 1 tipo, então se torna comum a 2, a 3, etc?

Por exemplo: (isto é apenas um exemplo, não o meu problema real)

Se vendermos móveis, poderemos vender cadeiras, lâmpadas, sofás, TVs, etc. O tipo de TV pode ser o único tipo que transportamos com consumo de energia. Então eu colocaria o atributo power_consumption no tv_type_table . Mas então começamos a carregar sistemas de home theater que também têm uma propriedade power_consumption . OK, é apenas um outro produto, então vou adicionar este campo ao stereo_type_table , já que provavelmente é mais fácil neste momento. Mas com o passar do tempo, à medida que começamos a carregar mais e mais eletrônicos, percebemos que o power_consumption é amplo o suficiente para estar na main_product_table . O que eu deveria fazer agora?

Adicione o campo ao main_product_table . Escreva um script para percorrer os componentes eletrônicos e coloque o valor correto de cada type_table na main_product_table . Em seguida, solte essa coluna de cada type_table .

Agora, se eu estivesse sempre usando a mesma class GetProductData para interagir com o database para obter as informações do produto; então, se qualquer alteração no código agora precisar de refatoração, ela deve ser apenas para essa class.

Você pode ter uma tabela Product e uma tabela ProductAdditionInfo separada com 3 colunas: ID do produto, nome da informação adicional, valor da informação adicional. Se a cor for usada por muitos, mas não por todos os tipos de Produtos, você poderá ter uma coluna anulável na tabela Product ou apenas colocá-la em ProductAdditionalInfo.

Essa abordagem não é uma técnica tradicional para um database relacional, mas eu já vi isso muito na prática. Pode ser flexível e ter bom desempenho.

Steve Yegge chama isso de padrão de propriedades e escreveu um longo post sobre como usá-lo.