Você tem pelo menos essas cinco opções para modelar a hierarquia de tipos que você descreve:
Herança de tabela única : uma tabela para todos os tipos de produtos, com colunas suficientes para armazenar todos os atributos de todos os tipos. Isso significa muitas colunas, a maioria das quais é NULL em qualquer linha.
Herança de tabela de classe : 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 armazenar os atributos específicos para cada tipo de produto. Você pode usar padrões de design sofisticados para descrever isso, como Fachada e Memento. Mas, independentemente de você ter um blob de atributos que não podem ser consultados facilmente no SQL; você precisa buscar o blob inteiro no aplicativo e classificá-lo por aí.
Valor de atributo da entidade : uma tabela para produtos e uma tabela que atribui atributos a linhas, em vez de colunas. O EAV não é um design 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 perguntas com a tag eav no StackOverflow para obter algumas das armadilhas.
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. Parece a solução mais flexível e, portanto, a melhor. No entanto, lembre-se do ditado TANSTAAFL . Aqui estão algumas das desvantagens do EAV:
- Não há como tornar uma coluna obrigatória (equivalente a
NOT NULL
).
- Não há como usar tipos de dados SQL para validar entradas.
- Não há como garantir que os nomes dos atributos sejam digitados de forma consistente.
- Não há como colocar uma chave estrangeira nos valores de qualquer atributo, por exemplo, para uma tabela de pesquisa.
- A busca de resultados em um layout tabular convencional é complexa e dispendiosa, porque para obter atributos de várias linhas, você precisa fazer
JOIN
para cada atributo.
O grau de flexibilidade que o EAV oferece exige 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 tipos de produtos, é muito mais simples criar uma tabela por tipo de produto para atributos específicos do produto, para que você tenha uma estrutura consistente imposta pelo menos para entradas do mesmo tipo de produto.
Eu usaria o EAV apenas se todas as linhas tiverem potencial para 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 classe seria minha primeira escolha.
Atualização 2019: quanto mais vejo as pessoas usando o JSON como uma solução para o problema de "muitos atributos personalizados", menos eu gosto dessa solução. Isso torna as consultas muito complexas, mesmo ao usar funções JSON especiais para suportá-las. É necessário muito mais espaço de armazenamento para armazenar documentos JSON, em vez de armazenar em linhas e colunas normais.
Basicamente, nenhuma dessas soluções é fácil ou eficiente em um banco de dados relacional. Toda a idéia de ter "atributos variáveis" está fundamentalmente em desacordo com a teoria relacional.
O que se resume é que você precisa escolher uma das soluções com base nas que são menos ruins para o seu aplicativo. Portanto, você precisa saber como vai consultar os dados antes de escolher um design de banco de dados. Não há como escolher uma solução que seja "melhor" porque qualquer uma das soluções pode ser a melhor para um determinado aplicativo.