Como você modelar atributos personalizados de entidades?

votos
5

Vamos dizer que nós estamos tendo uma aplicação que deve ser capaz de armazenar todo o tipo de produtos. Cada produto tem pelo menos um IDe uma Name, mas todos os outros atributos podem ser definidos pelo próprio usuário.

  1. Por exemplo, ele poderia criar um productgroup Ipods que conteria atributos capacidade e geração
  2. Por exemplo, ele poderia criar um ProductGroup TShirts com os atributos tamanho e cor
  3. Precisamos armazenar a definição de um produto e os produtos de concreto em si.
  4. Queremos garantir que ele é facilmente possível agregar (GROUP BY) por atributos do produto. Por exemplo, selecionar a soma total de capacidade para cada geração de iPods
  5. A solução não deve exigir alterações de esquema (requisito adicional devido à entrada de Bill Karwin - ver sua resposta também!)

Como você modelar seu esquema no que diz respeito aos requisitos acima?

Nota: requirment 4. é importante!

Obrigado a todos para contribuir e discutir a abordagem. Eu vi algumas soluções para este problema no passado, mas nenhum deles fez agrupamento fácil para mim :(

Publicado 19/05/2009 em 18:24
fonte usuário
Em outras línguas...                            


5 respostas

votos
11

Eu recomendo tanto o Herança de tabelas de concreto ou as de herança de classe Tabela projetos. Ambos os projetos satisfazer todos os quatro de seus critérios.

Em concreto Tabela Herança:

  1. Ipods são armazenados na tabela product_ipodscom colunas ID, Name, Capacity, Generation.
  2. T-shirts são armazenados na tabela product_tshirtscom colunas ID, Name, Size, Color.
  3. A definição dos tipos de produtos de concreto são nos metadados (definições de tabela) de product_ipodse product_tshirts.
  4. SELECT SUM(Capacity) FROM product_ipods GROUP BY Generation;

Em Class Herança Tabela:

  1. Atributos de produtos genéricos são armazenados na tabela Productscom colunas ID, Name.

    Ipods são armazenados na tabela product_ipodscom colunas product_id(chave estrangeira para Products.ID), Capacity, Generation.

  2. T-shirts são armazenados na tabela product_tshirtscom colunas product_id(chave estrangeira para Products.ID), Size, Color.
  3. A definição dos tipos de produtos de concreto são nos metadados (definições de tabela) de products, product_ipodse product_tshirts.
  4. SELECT SUM(Capacity) FROM product_ipods GROUP BY Generation;

Veja também a minha resposta a " mesa de Produto, muitos tipos de produto, cada produto tem muitos parâmetros " onde eu descrevem várias soluções para o tipo de problema que você está descrevendo. Eu também entrar em detalhes sobre exatamente por isso que EAV é um projeto quebrado.


Re comentário de @dcolumbus:

Com CTI, que cada linha dos product_ipods ser uma variação com o seu próprio preço?

Eu esperaria a coluna preço para aparecer na productstabela, se todo o tipo de produto tem um preço. Com CTI, as tabelas de tipo de produto normalmente só tem colunas para atributos que pertencem apenas a esse tipo de produto. Quaisquer atributos comuns a todos os tipos de produtos obter colunas na tabela pai.

Além disso, ao armazenar itens de pedidos, você iria em seguida, armazenar a linha de product_ipods como o item de linha?

Em uma tabela de linha-itens, armazenar a identificação do produto, que deve ser o mesmo valor, tanto na productsmesa e a product_ipodsmesa.


Re comentários de @dcolumbus:

Isso parece tão redundante para mim ... nesse cenário, não vejo o ponto do sub-mesa. Mas mesmo que o sub-tabela faz sentido, qual é a conexão id?

O ponto do sub-tabela é para armazenar colunas que não são necessários por todos os outros tipos de produtos.

O ID de ligação pode ser um número auto-incremento. A tabela sub-tipo não precisa de auto-incremento seu próprio id, porque ele pode apenas usar o valor gerado pela super-mesa.

CREATE TABLE products (
  product_id INT AUTO_INCREMENT PRIMARY KEY,
  sku VARCHAR(30) NOT NULL,
  name VARCHAR(100) NOT NULL,
  price NUMERIC(9,2) NOT NULL
);

CREATE TABLE product_ipods (
  product_id INT PRIMARY KEY,
  size TINYINT DEFAULT 16,
  color VARCHAR(10) DEFAULT 'silver',
  FOREIGN KEY (product_id) REFERENCES products(product_id)
);

INSERT IGNORE  INTO products (sku, name, price) VALUES ('IPODS1C1', 'iPod Touch', 229.00);
INSERT IGNORE  INTO product_ipods VALUES (LAST_INSERT IGNORE _ID(), 16, 'silver');
INSERT IGNORE  INTO products (sku, name, price) VALUES ('IPODS1C2', 'iPod Touch', 229.00);
INSERT IGNORE  INTO product_ipods VALUES (LAST_INSERT IGNORE _ID(), 16, 'black');
INSERT IGNORE  INTO products (sku, name, price) VALUES ('IPODS1C3', 'iPod Touch', 229.00);
INSERT IGNORE  INTO product_ipods VALUES (LAST_INSERT IGNORE _ID(), 16, 'red');
INSERT IGNORE  INTO products (sku, name, price) VALUES ('IPODS2C1', 'iPod Touch', 299.00);
INSERT IGNORE  INTO product_ipods VALUES (LAST_INSERT IGNORE _ID(), 32, 'silver');
INSERT IGNORE  INTO products (sku, name, price) VALUES ('IPODS2C2', 'iPod Touch', 299.00);
INSERT IGNORE  INTO product_ipods VALUES (LAST_INSERT IGNORE _ID(), 32, 'silver');
INSERT IGNORE  INTO products (sku, name, price) VALUES ('IPODS2C3', 'iPod Touch', 299.00);
INSERT IGNORE  INTO product_ipods VALUES (LAST_INSERT IGNORE _ID(), 32, 'red');
Respondeu 19/05/2009 em 19:16
fonte usuário

votos
3

O agrupamento não vai ser fácil, porque o operador agregado que você vai usar em "cor"? Note que não é possível usar sua exigência 4 no caso 2.

Em qualquer caso, a agregação só é difícil por causa da variação nos tipos de dados e pode ser atenuada por abordá-lo de uma forma mais typesafe - sabendo que ele nunca faz sentido para adicionar maçãs e laranjas.

Este é o modelo EAV clássico e tem um lugar em bancos de dados, onde cuidadosamente projetados. A fim de fazer typesafe-lo um pouco mais, eu já vi casos em que os valores são armazenados em typesafe mesas em vez de em uma única coluna varchar forma livre.

Em vez de Valores:

EntityID int
,AttributeID int
,Value varchar(255)

Você tem várias tabelas:

EntityID int
,AttributeID int
,ValueMoney money

EntityID int
,AttributeID int
,ValueInt int

etc.

Em seguida, para obter a sua capacidade de iPod por geração:

SELECT vG.ValueVarChar AS Generation, SUM(vC.ValueDecimal) AS TotalCapacity
FROM Products AS p
INNER JOIN Attributes AS aG
    ON aG.AttributeName = 'generation'
INNER JOIN ValueVarChar AS vG
    ON vG.EntityID = p.ProductID
    AND vG.AttributeID = aG.AttributeID
INNER JOIN Attributes AS aC
    ON aC.AttributeName = 'capacity'
INNER JOIN ValueDecimal AS vC
    ON vC.EntityID = p.ProductID
    AND vC.AttributeID = aC.AttributeID
GROUP BY vG.ValueVarChar
Respondeu 19/05/2009 em 18:50
fonte usuário

votos
0

Pergunto-me como superar problemas com o uso padrão BLOB como uma alternativa de EAV. Vamos supor que poderíamos armazenar todos os campos personalizados da entidade em um campo como uma string porexemplo em JSON algo como tihis: {CustomField1: value1, customField2: value2, ..., customFieldN: valorN}

Como superar os seguintes problemas: 1. Como seach por campos personalizados separadas, por exemplo, para encontrar entidades com as condições custField1 = value1 E customField2 = valor2? 2. Como mantain a integridade dos dados, por exemplo, se excluir um campo personalizado para a entidade como excluir todos os valores oif esses campos personalizados na entidade.

Respondeu 16/11/2010 em 08:43
fonte usuário

votos
0

Parece que você está olhando para criar um banco de dados de catálogo de produtos.

Eu recomendo esta abordagem. http://edocs.bea.com/wlp/docs40/catalog/schemcat.htm

Respondeu 19/05/2009 em 18:42
fonte usuário

votos
0

CREATE TABLE para os novos produtos e ALTER TABLE por adição / remoção de colunas como o utilizador executa as operações. Use o esquema de saber quais as propriedades de cada produto tem. Isso satisfaz todos os quatro de suas necessidades.

Você também precisará de uma tabela para armazenar os outros nomes de tabelas ou prefixar as tabelas com algo que você pode consultar contra sysobjects para as tabelas:

select [name] from sysobjects where [name] like 'product_%' AND xtype='U'
Respondeu 19/05/2009 em 18:37
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more