Esquema proposto
Em primeiro lugar, aqui está um exemplo do meu esquema proposto para referência ao longo do meu post:
Clothes
----------
ClothesID (PK) INT NOT NULL
Name VARCHAR(50) NOT NULL
Color VARCHAR(50) NOT NULL
Price DECIMAL(5,2) NOT NULL
BrandID INT NOT NULL
...
Brand_1
--------
ClothesID (FK/PK) int NOT NULL
ViewingUrl VARCHAR(50) NOT NULL
SomeOtherBrand1SpecificAttr VARCHAR(50) NOT NULL
Brand_2
--------
ClothesID (FK/PK) int NOT NULL
PhotoUrl VARCHAR(50) NOT NULL
SomeOtherBrand2SpecificAttr VARCHAR(50) NOT NULL
Brand_X
--------
ClothesID (FK/PK) int NOT NULL
SomeOtherBrandXSpecificAttr VARCHAR(50) NOT NULL
declaração do problema
Eu tenho uma tabela de roupas que possui colunas como nome, cor, preço, brandid e assim por diante para descrever os atributos de uma determinada peça de roupa.
Aqui está o meu problema: diferentes marcas de roupas requerem informações diferentes. Qual é a melhor prática para lidar com um problema como esse?
Observe que, para meus propósitos, é necessário encontrar informações específicas da marca a partir de uma entrada de roupas . Isso ocorre porque primeiro mostro as informações de uma entrada de roupas para o usuário, após o que devo usar as informações específicas da marca para comprar o item. Em resumo, deve haver uma relação direcional entre as roupas (de) e as tabelas brand_x .
Solução proposta/atual
Para lidar com isso, pensei no seguinte esquema de design:
A tabela de roupas terá uma coluna de marca que pode ter valores de id variando de 1 a x, onde um determinado id corresponde a uma tabela específica da marca. Por exemplo, o valor de id 1 corresponderá à tabela brand_1 (que pode ter uma coluna de url ), o id 2 corresponderá a brand_2 (que pode ter uma coluna de fornecedor ), etc.
Assim, para associar uma determinada entrada de roupas com suas informações específicas da marca, imagino que a lógica no nível do aplicativo seja algo como isto:
clothesId = <some value>
brand = query("SELECT brand FROM clothes WHERE id = clothesId")
if (brand == 1) {
// get brand_1 attributes for given clothesId
} else if (brand == 2) {
// get brand_2 attributes for given clothesId
} ... etc.
Outros comentários e pensamentos
Estou tentando normalizar todo o meu banco de dados no BCNF e, embora seja isso que eu criei, o código do aplicativo resultante me deixa muito ansioso. Não há como impor relações, exceto no nível do aplicativo e, portanto, o design parece muito hackeado e, prevejo, muito propenso a erros.
Pesquisar
Certifiquei-me de olhar as entradas anteriores antes de fazer uma postagem. Aqui está um post com um problema quase idêntico que consegui encontrar. Eu fiz este post de qualquer maneira porque parece que a única resposta fornecida não tem uma solução SQL ou baseada em design (ou seja, menciona OOP, herança e interfaces).
Também sou um novato quando se trata de design de banco de dados e, portanto, gostaria de receber informações.
Parece que há respostas mais úteis no Stack Overflow:
- Aqui
- E aqui
- Aaaaand aqui (o conceito-chave é: herança de tabela de classe)
Eu me referi às soluções lá e sugiro que outras pessoas que encontrem minha pergunta também o façam.
Apesar dos links fornecidos acima, ainda estou procurando respostas aqui e agradeceria qualquer solução fornecida!
Estou usando o PostgreSQL.
Pessoalmente, não gosto de usar um esquema de várias tabelas para essa finalidade.
Eu configurei uma amostra dbfiddle .
Meu esquema de tabela proposto:
Deixe-me inserir alguns dados:
Se você precisar buscar atributos comuns:
Ou você pode facilmente obter Roupas por Marca:
Mas para mim, um dos melhores desse esquema é que você pode filtrar por Atributos:
Usando um esquema multi-tabela, qualquer uma das consultas anteriores exigirá lidar com um número ilimitado de tabelas ou com campos XML ou JSON.
Outra opção com este esquema é que você pode definir modelos, por exemplo, você pode adicionar uma nova tabela BrandAttrTemplates. Cada vez que você adiciona um novo registro, você pode usar um gatilho ou um SP para gerar um conjunto de atributos predefinidos para esta Filial.
Sinto muito, gostaria de estender minhas explicações, acho que é mais claro do que meu inglês.
Atualizar
Minha resposta atual deve funcionar, independentemente de qual RDBMS. De acordo com seus comentários, se você precisar filtrar os valores dos atributos, sugiro pequenas alterações.
Como o MS-Sql não permite arrays, configurei uma nova amostra mantendo o mesmo esquema de tabela, mas alterando AttrValue para um tipo de campo ARRAY.
Na verdade, usando o POSTGRES, você pode aproveitar essa matriz usando um índice GIN.
(Deixe-me dizer que @EvanCarrol tem um bom conhecimento sobre Postgres, certamente melhor do que eu. Mas deixe-me adicionar minha parte.)
Agora, você também pode consultar usando valores de atributos individuais como:
Este é o resultado:
Usando JSON e PostgreSQL
Acho que você está tornando isso mais difícil do que precisa ser e será mordido por isso mais tarde. Você não precisa do modelo de valor de atributo de entidade, a menos que realmente precise de EAV.
Não há absolutamente nada de errado com esse esquema.
Agora você pode consultá-lo usando uma junção simples
E qualquer um dos operadores JSON funciona em uma cláusula where.
Como observação, não coloque os URLs no banco de dados. Eles mudam com o tempo. Basta criar uma função que os leve.
como queiras. Se você estiver usando o PostgreSQL, pode até usar hashids .
Também digno de nota,
jsonb
é armazenado como binário (portanto, o -'b') e também é indexável, ou SARGable ou qualquer outra coisa que os garotos legais estejam chamando atualmente:CREATE INDEX ON brands USING gin ( attributes );
A diferença aqui está na simplicidade da consulta..
How about a different one..
What you are describing is, at least in part, a product catalog. You have several attributes which are common to all products. These belong in a well normalized table.
Beyond that, you have a series of attributes which are brand specific (and I expect could be product specific). What does your system need to do with these specific attributes? Do you have business logic that depends on the schema of these attributes or are you just listing them in a series of "label":"value" pairs?
Other answers are suggesting using what is essentially a CSV approach (whether this is
JSON
orARRAY
or otherwise) - These approaches forego regular relational schema handling by moving the schema out of metadata and into the data itself.There is a portable design pattern for this which fits relational databases very well. It is EAV (entity-attribute-value). I'm sure you've read in many, many places that "EAV is Evil" (and it is). However, there is one particular application where the problems with EAV are not important, and that is product attribute catalogs.
All of the usual arguments against EAV don't apply to a product feature catalog, since product feature values are generally only regurgitated into a list or worst case into a comparison table.
Using a
JSON
column type takes your ability to enforce any data constraints out of the database and forces it into your application logic. Also, using one attributes table for every brand has the following disadvantages:It is not especially difficult to retrieve data about a product with brand-specific features. It is arguably easier to create a dynamic SQL using the EAV model than it would be using the table-per-category model. In table-per-category, you need reflection (or your
JSON
) to find out what the feature column names are. Then you can build a list of items for a where clause. In the EAV model, theWHERE X AND Y AND Z
becomesINNER JOIN X INNER JOIN Y INNER JOIN Z
, so the query is a little more complicated, but the logic to build the query is still totally table-driven and it will be more than scalable enough if you have the proper indexes built.There are a lot of reasons not to use EAV as a general approach. Those reasons don't apply to a product feature catalog so there is nothing wrong with EAV in this specific application.
To be sure, this is a short answer for a complex and controversial topic. I have answered similar questions before and gone into more detail about the general aversion to EAV. For example:
I would say EAV is used less often lately than it used to be, for mostly good reasons. However, I think it is also not well understood.
Uma solução fácil é incluir todos os atributos possíveis como colunas na mesa de roupas principal e tornar todas as colunas específicas da marca anuláveis. Esta solução quebra a normalização do banco de dados, mas é muito fácil de implementar.