如何为实体的自定义属性建模?

时间:2009-05-19 17:24:37

标签: sql sql-server database-design schema entity-attribute-value

假设我们有一个应该能够存储所有类型产品的应用程序。每个产品至少有一个ID和一个Name,但所有其他属性都可以由用户自己定义。

  1. E.g。他可以创建一个产品组 Ipods ,其中包含属性 capacity generation
  2. E.g。他可以创建一个产品组 TShirts ,其属性为 size color
  3. 我们需要存储产品的定义和具体产品本身。
  4. 我们希望确保按产品属性轻松聚合(GROUP BY)。例如。选择每代ipods的总容量
  5. 解决方案不得要求架构更改(由于Bill Karwin的输入而增加了要求 - 请参阅他的答案!)
  6. 您如何根据上述要求对架构进行建模?

    注意:要求4.很重要!

    感谢大家的贡献和讨论方法。我在过去看到过这个问题的一些解决方案,但没有一个能让我轻松分组:(

5 个答案:

答案 0 :(得分:11)

我建议Concrete Table InheritanceClass Table Inheritance设计。两种设计都满足您的所有四个标准。

在具体表继承中:

  1. Ipods存储在表product_ipods中,列IDNameCapacityGeneration
  2. T恤存储在表格product_tshirts中,其中包含IDNameSizeColor列。
  3. 具体产品类型的定义位于product_ipodsproduct_tshirts的元数据(表定义)中。
  4. SELECT SUM(Capacity) FROM product_ipods GROUP BY Generation;
  5. 在类表继承中:

    1. 通用产品属性存储在表Products中,列IDName

      Ipods存储在表product_ipods中,列product_idProducts.ID的外键),CapacityGeneration

    2. T恤存储在表格product_tshirts中,其中包含product_id列(Products.ID的外键),SizeColor
    3. 具体产品类型的定义位于productsproduct_ipodsproduct_tshirts的元数据(表格定义)中。
    4. SELECT SUM(Capacity) FROM product_ipods GROUP BY Generation;

    5. 另请参阅我对“Product table, many kinds of product, each product has many parameters”的回答,其中我描述了您所描述的问题类型的几种解决方案。我还详细介绍了为什么EAV是一个破碎的设计。


      来自@dcolumbus的评论:

        

      对于CTI,product_ipods的每一行都是一个变量,它的价格是多少?

      如果每种类型的产品都有价格,我希望价格列会显示在products表格中。对于CTI,产品类型表通常只包含仅适用于该类型产品的属性列。所有产品类型共有的任何属性都会获得父表中的列。

        

      此外,在存储订单行项目时,您是否会将product_ipods中的行存储为订单项?

      在行项目表中,存储产品ID,该值在products表和product_ipods表中应该是相同的值。


      来自@dcolumbus的评论:

        

      这对我来说似乎是多余的......在那种情况下,我没有看到子表的重点。但即使子表确实有意义,连接id是什么?

      子表的要点是存储所有其他产品类型不需要的列。

      连接ID可以是自动递增编号。子类型表不需要自动递增自己的id,因为它只能使用超级表生成的值。

      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 INTO products (sku, name, price) VALUES ('IPODS1C1', 'iPod Touch', 229.00);
      INSERT INTO product_ipods VALUES (LAST_INSERT_ID(), 16, 'silver');
      INSERT INTO products (sku, name, price) VALUES ('IPODS1C2', 'iPod Touch', 229.00);
      INSERT INTO product_ipods VALUES (LAST_INSERT_ID(), 16, 'black');
      INSERT INTO products (sku, name, price) VALUES ('IPODS1C3', 'iPod Touch', 229.00);
      INSERT INTO product_ipods VALUES (LAST_INSERT_ID(), 16, 'red');
      INSERT INTO products (sku, name, price) VALUES ('IPODS2C1', 'iPod Touch', 299.00);
      INSERT INTO product_ipods VALUES (LAST_INSERT_ID(), 32, 'silver');
      INSERT INTO products (sku, name, price) VALUES ('IPODS2C2', 'iPod Touch', 299.00);
      INSERT INTO product_ipods VALUES (LAST_INSERT_ID(), 32, 'silver');
      INSERT INTO products (sku, name, price) VALUES ('IPODS2C3', 'iPod Touch', 299.00);
      INSERT INTO product_ipods VALUES (LAST_INSERT_ID(), 32, 'red');
      

答案 1 :(得分:3)

分组并不容易,因为你要在“颜色”上使用哪个聚合运算符?请注意,在案例2中无法使用您的要求4。

在任何情况下,聚合都很困难,因为数据类型的变化,并且可以通过以更加类型安全的方式接近它来减轻 - 知道添加苹果和橙子是没有意义的。

这是经典的EAV模型,它在精心设计的数据库中占有一席之地。为了使它更加类型安全,我已经看到了这些值存储在类型安全表而不是单个自由格式varchar列中的情况。

而不是值:

EntityID int
,AttributeID int
,Value varchar(255)

您有多个表格:

EntityID int
,AttributeID int
,ValueMoney money

EntityID int
,AttributeID int
,ValueInt int

etc.

然后每代获得你的iPod容量:

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

答案 2 :(得分:0)

通过在用户执行操作时添加/删除列来为新产品和ALTER TABLE创建表。使用架构了解每个产品具有哪些属性。这满足了您的所有四项要求。

您还需要一个表来存储其他表名或表的前缀,以及可以针对表查询sysobjects的内容:

select [name] from sysobjects where [name] like 'product_%' AND xtype='U'

答案 3 :(得分:0)

听起来您正在设计产品目录数据库。

我推荐这种方法。 http://edocs.bea.com/wlp/docs40/catalog/schemcat.htm

答案 4 :(得分:0)

我想知道如何克服使用BLOB模式作为EAV的替代方案的问题。 假设我们可以将实体的所有自定义字段存储在一个字段中作为字符串,例如在JSON中,例如tihis: {customField1:value1,customField2:value2,..., customFieldN:valueN}

如何克服以下问题: 1.如何通过单独的自定义字段进行搜索,例如,查找条件为custField1 = value1 AND customField2 = value2的实体? 2.如何保持数据完整性,例如,如果我们删除实体的自定义字段,如何删除实体中这些自定义字段的所有值。